From c59f9ad7768a007ca7a49ea11b16617529e86d52 Mon Sep 17 00:00:00 2001 From: Jo Asplin Date: Tue, 6 Sep 2011 13:46:40 +0200 Subject: Moved network autotests into new directory structure Task-number: QTBUG-21223 Change-Id: I55dbf5c42a1c5d938b9e0c9bf7d90457a6c26bbc Reviewed-on: http://codereview.qt-project.org/4259 Reviewed-by: Qt Sanity Bot Reviewed-by: Sergio Ahumada Reviewed-by: Rohan McGovern --- tests/auto/network/access/access.pro | 20 + .../access/qabstractnetworkcache/.gitignore | 1 + .../qabstractnetworkcache.pro | 14 + .../tests/httpcachetest_cachecontrol-expire.cgi | 7 + .../tests/httpcachetest_cachecontrol.cgi | 13 + .../tests/httpcachetest_cachecontrol200.cgi | 9 + .../tests/httpcachetest_etag200.cgi | 5 + .../tests/httpcachetest_etag304.cgi | 11 + .../tests/httpcachetest_expires200.cgi | 5 + .../tests/httpcachetest_expires304.cgi | 11 + .../tests/httpcachetest_expires500.cgi | 11 + .../tests/httpcachetest_lastModified200.cgi | 5 + .../tests/httpcachetest_lastModified304.cgi | 11 + .../tst_qabstractnetworkcache.cpp | 416 + tests/auto/network/access/qftp/.gitattributes | 1 + tests/auto/network/access/qftp/.gitignore | 2 + tests/auto/network/access/qftp/qftp.pro | 22 + tests/auto/network/access/qftp/rfc3252.txt | 899 + tests/auto/network/access/qftp/tst_qftp.cpp | 2170 +++ tests/auto/network/access/qhttp/.gitattributes | 1 + tests/auto/network/access/qhttp/.gitignore | 1 + tests/auto/network/access/qhttp/dummyserver.h | 114 + tests/auto/network/access/qhttp/qhttp.pro | 31 + tests/auto/network/access/qhttp/rfc3252.txt | 899 + tests/auto/network/access/qhttp/trolltech | 8 + tests/auto/network/access/qhttp/tst_qhttp.cpp | 1576 ++ .../qhttp/webserver/cgi-bin/retrieve_testfile.cgi | 6 + .../network/access/qhttp/webserver/cgi-bin/rfc.cgi | 5 + .../qhttp/webserver/cgi-bin/store_testfile.cgi | 6 + .../auto/network/access/qhttp/webserver/index.html | 899 + tests/auto/network/access/qhttp/webserver/rfc3252 | 899 + .../network/access/qhttp/webserver/rfc3252.txt | 899 + .../access/qhttpnetworkconnection/.gitignore | 1 + .../qhttpnetworkconnection.pro | 13 + .../tst_qhttpnetworkconnection.cpp | 1131 ++ .../network/access/qhttpnetworkreply/.gitignore | 1 + .../access/qhttpnetworkreply/qhttpnetworkreply.pro | 7 + .../qhttpnetworkreply/tst_qhttpnetworkreply.cpp | 134 + .../qnetworkaccessmanager.pro | 6 + .../tst_qnetworkaccessmanager.cpp | 111 + .../access/qnetworkcachemetadata/.gitignore | 1 + .../qnetworkcachemetadata.pro | 7 + .../tst_qnetworkcachemetadata.cpp | 373 + .../auto/network/access/qnetworkcookie/.gitignore | 1 + .../access/qnetworkcookie/qnetworkcookie.pro | 5 + .../access/qnetworkcookie/tst_qnetworkcookie.cpp | 733 + .../network/access/qnetworkcookiejar/.gitignore | 1 + .../access/qnetworkcookiejar/qnetworkcookiejar.pro | 5 + .../qnetworkcookiejar/tst_qnetworkcookiejar.cpp | 446 + .../network/access/qnetworkdiskcache/.gitignore | 1 + .../access/qnetworkdiskcache/qnetworkdiskcache.pro | 7 + .../qnetworkdiskcache/tst_qnetworkdiskcache.cpp | 677 + .../network/access/qnetworkreply/.gitattributes | 3 + tests/auto/network/access/qnetworkreply/.gitignore | 3 + tests/auto/network/access/qnetworkreply/bigfile | 17980 +++++++++++++++++++ .../access/qnetworkreply/certs/aspiriniks.ca.crt | 22 + .../network/access/qnetworkreply/certs/fluke.cert | 75 + .../network/access/qnetworkreply/certs/fluke.key | 15 + .../qnetworkreply/certs/qt-test-server-cacert.pem | 17 + .../network/access/qnetworkreply/certs/server.key | 15 + .../network/access/qnetworkreply/certs/server.pem | 24 + .../network/access/qnetworkreply/echo/echo.pro | 6 + .../network/access/qnetworkreply/echo/main.cpp | 62 + tests/auto/network/access/qnetworkreply/empty | 0 tests/auto/network/access/qnetworkreply/image1.jpg | Bin 0 -> 1045459 bytes tests/auto/network/access/qnetworkreply/image2.jpg | Bin 0 -> 879218 bytes tests/auto/network/access/qnetworkreply/image3.jpg | Bin 0 -> 887729 bytes .../network/access/qnetworkreply/qnetworkreply.pro | 7 + .../network/access/qnetworkreply/qnetworkreply.qrc | 5 + tests/auto/network/access/qnetworkreply/resource | 283 + .../auto/network/access/qnetworkreply/rfc3252.txt | 899 + .../auto/network/access/qnetworkreply/smb-file.txt | 1 + .../network/access/qnetworkreply/test/test.pro | 38 + .../access/qnetworkreply/tst_qnetworkreply.cpp | 6342 +++++++ .../auto/network/access/qnetworkrequest/.gitignore | 1 + .../access/qnetworkrequest/qnetworkrequest.pro | 5 + .../access/qnetworkrequest/tst_qnetworkrequest.cpp | 496 + tests/auto/network/bearer/bearer.pro | 6 + tests/auto/network/bearer/qbearertestcommon.h | 87 + .../qnetworkconfiguration.pro | 15 + .../tst_qnetworkconfiguration.cpp | 311 + .../qnetworkconfigurationmanager.pro | 15 + .../tst_qnetworkconfigurationmanager.cpp | 378 + .../bearer/qnetworksession/lackey/lackey.pro | 15 + .../network/bearer/qnetworksession/lackey/main.cpp | 155 + .../bearer/qnetworksession/qnetworksession.pro | 3 + .../network/bearer/qnetworksession/test/test.pro | 26 + .../qnetworksession/test/tst_qnetworksession.cpp | 1683 ++ tests/auto/network/kernel/kernel.pro | 14 + .../kernel/qauthenticator/qauthenticator.pro | 5 + .../kernel/qauthenticator/tst_qauthenticator.cpp | 155 + tests/auto/network/kernel/qhostaddress/.gitignore | 1 + .../network/kernel/qhostaddress/qhostaddress.pro | 15 + .../kernel/qhostaddress/tst_qhostaddress.cpp | 611 + tests/auto/network/kernel/qhostinfo/.gitignore | 1 + tests/auto/network/kernel/qhostinfo/qhostinfo.pro | 16 + .../network/kernel/qhostinfo/tst_qhostinfo.cpp | 665 + .../network/kernel/qnetworkaddressentry/.gitignore | 1 + .../qnetworkaddressentry/qnetworkaddressentry.pro | 6 + .../tst_qnetworkaddressentry.cpp | 185 + .../network/kernel/qnetworkinterface/.gitignore | 1 + .../kernel/qnetworkinterface/qnetworkinterface.pro | 7 + .../qnetworkinterface/tst_qnetworkinterface.cpp | 241 + tests/auto/network/kernel/qnetworkproxy/.gitignore | 1 + .../network/kernel/qnetworkproxy/qnetworkproxy.pro | 11 + .../kernel/qnetworkproxy/tst_qnetworkproxy.cpp | 118 + .../network/kernel/qnetworkproxyfactory/.gitignore | 1 + .../qnetworkproxyfactory/qnetworkproxyfactory.pro | 11 + .../tst_qnetworkproxyfactory.cpp | 275 + tests/auto/network/network.pro | 8 + .../network/socket/platformsocketengine/.gitignore | 1 + .../platformsocketengine/platformsocketengine.pri | 19 + .../platformsocketengine/platformsocketengine.pro | 16 + .../tst_platformsocketengine.cpp | 763 + .../auto/network/socket/qabstractsocket/.gitignore | 1 + .../socket/qabstractsocket/qabstractsocket.pro | 11 + .../socket/qabstractsocket/tst_qabstractsocket.cpp | 109 + .../network/socket/qhttpsocketengine/.gitignore | 1 + .../socket/qhttpsocketengine/qhttpsocketengine.pro | 13 + .../qhttpsocketengine/tst_qhttpsocketengine.cpp | 748 + tests/auto/network/socket/qlocalsocket/.gitignore | 2 + .../socket/qlocalsocket/example/client/client.pro | 10 + .../socket/qlocalsocket/example/client/main.cpp | 84 + .../socket/qlocalsocket/example/example.pro | 3 + .../socket/qlocalsocket/example/server/main.cpp | 97 + .../socket/qlocalsocket/example/server/server.pro | 13 + .../network/socket/qlocalsocket/lackey/lackey.pro | 16 + .../network/socket/qlocalsocket/lackey/main.cpp | 296 + .../socket/qlocalsocket/lackey/scripts/client.js | 35 + .../socket/qlocalsocket/lackey/scripts/server.js | 19 + .../network/socket/qlocalsocket/qlocalsocket.pro | 4 + .../auto/network/socket/qlocalsocket/test/test.pro | 50 + .../socket/qlocalsocket/tst_qlocalsocket.cpp | 1120 ++ .../network/socket/qsocks5socketengine/.gitignore | 1 + .../qsocks5socketengine/qsocks5socketengine.pro | 17 + .../tst_qsocks5socketengine.cpp | 963 + tests/auto/network/socket/qtcpserver/.gitignore | 3 + .../qtcpserver/crashingServer/crashingServer.pro | 9 + .../socket/qtcpserver/crashingServer/main.cpp | 70 + .../auto/network/socket/qtcpserver/qtcpserver.pro | 4 + tests/auto/network/socket/qtcpserver/test/test.pro | 37 + .../network/socket/qtcpserver/tst_qtcpserver.cpp | 817 + tests/auto/network/socket/qtcpsocket/.gitignore | 3 + .../auto/network/socket/qtcpsocket/qtcpsocket.pro | 8 + .../network/socket/qtcpsocket/stressTest/Test.cpp | 146 + .../network/socket/qtcpsocket/stressTest/Test.h | 95 + .../network/socket/qtcpsocket/stressTest/main.cpp | 67 + .../socket/qtcpsocket/stressTest/stressTest.pro | 12 + tests/auto/network/socket/qtcpsocket/test/test.pro | 33 + .../network/socket/qtcpsocket/tst_qtcpsocket.cpp | 2684 +++ tests/auto/network/socket/qudpsocket/.gitignore | 2 + .../qudpsocket/clientserver/clientserver.pro | 8 + .../socket/qudpsocket/clientserver/main.cpp | 170 + .../auto/network/socket/qudpsocket/qudpsocket.pro | 4 + tests/auto/network/socket/qudpsocket/test/test.pro | 28 + .../network/socket/qudpsocket/tst_qudpsocket.cpp | 1356 ++ .../network/socket/qudpsocket/udpServer/main.cpp | 90 + .../socket/qudpsocket/udpServer/udpServer.pro | 7 + tests/auto/network/socket/socket.pro | 17 + tests/auto/network/ssl/qsslcertificate/.gitignore | 1 + .../ssl/qsslcertificate/certificates/ca-cert.pem | 33 + .../certificates/ca-cert.pem.digest-md5 | 1 + .../certificates/ca-cert.pem.digest-sha1 | 1 + .../certificates/cert-ss-san-utf8.pem | 16 + .../certificates/cert-ss-san-utf8.pem.san | 5 + .../qsslcertificate/certificates/cert-ss-san.pem | 13 + .../certificates/cert-ss-san.pem.san | 5 + .../ssl/qsslcertificate/certificates/cert-ss.der | Bin 0 -> 461 bytes .../certificates/cert-ss.der.pubkey | Bin 0 -> 162 bytes .../ssl/qsslcertificate/certificates/cert-ss.pem | 12 + .../certificates/cert-ss.pem.digest-md5 | 1 + .../certificates/cert-ss.pem.digest-sha1 | 1 + .../certificates/cert-ss.pem.pubkey | 6 + .../ssl/qsslcertificate/certificates/cert.der | Bin 0 -> 503 bytes .../qsslcertificate/certificates/cert.der.pubkey | Bin 0 -> 162 bytes .../ssl/qsslcertificate/certificates/cert.pem | 13 + .../certificates/cert.pem.digest-md5 | 1 + .../certificates/cert.pem.digest-sha1 | 1 + .../qsslcertificate/certificates/cert.pem.pubkey | 6 + .../certificates/gencertificates.sh | 104 + .../ssl/qsslcertificate/certificates/san.cnf | 5 + .../more-certificates/badguy-nul-cn.crt | 81 + .../more-certificates/badguy-nul-san.crt | 83 + .../blacklisted-google.com-diginotar.pem | 30 + .../more-certificates/blacklisted1.pem | 19 + .../more-certificates/blacklisted2.pem | 19 + .../more-certificates/blacklisted3.pem | 19 + .../more-certificates/blacklisted4.pem | 19 + .../more-certificates/blacklisted5.pem | 19 + .../more-certificates/blacklisted6.pem | 19 + .../more-certificates/blacklisted7.pem | 19 + .../more-certificates/blacklisted8.pem | 19 + .../more-certificates/blacklisted9.pem | 19 + .../cert-large-expiration-date.pem | 15 + .../cert-large-expiration-date.txt.0.9.8 | 42 + .../cert-large-expiration-date.txt.1.0.0 | 42 + .../more-certificates/cert-large-serial-number.pem | 14 + .../malformed-just-begin-no-newline.pem | 1 + .../more-certificates/malformed-just-begin.pem | 1 + .../more-certificates/natwest-banking.pem | 36 + .../more-certificates/no-ending-newline.pem | 13 + .../more-certificates/test-cn-two-cns-cert.pem | 67 + .../more-certificates/test-cn-with-drink-cert.pem | 66 + .../more-certificates/trailing-whitespace.pem | 13 + .../ssl/qsslcertificate/qsslcertificate.pro | 29 + .../ssl/qsslcertificate/tst_qsslcertificate.cpp | 995 + .../ssl/qsslcertificate/verify-certs/README | 2 + .../ssl/qsslcertificate/verify-certs/cacert.pem | 23 + .../verify-certs/test-addons-mozilla-org-cert.pem | 34 + .../verify-certs/test-intermediate-ca-cert.pem | 66 + .../verify-certs/test-intermediate-is-ca-cert.pem | 53 + .../verify-certs/test-intermediate-not-ca-cert.pem | 54 + .../verify-certs/test-ocsp-good-cert.pem | 67 + tests/auto/network/ssl/qsslcipher/.gitignore | 1 + tests/auto/network/ssl/qsslcipher/qsslcipher.pro | 18 + .../auto/network/ssl/qsslcipher/tst_qsslcipher.cpp | 101 + tests/auto/network/ssl/qsslerror/.gitignore | 1 + tests/auto/network/ssl/qsslerror/qsslerror.pro | 18 + tests/auto/network/ssl/qsslerror/tst_qsslerror.cpp | 122 + tests/auto/network/ssl/qsslkey/.gitignore | 1 + .../auto/network/ssl/qsslkey/keys/dsa-pri-1024.der | Bin 0 -> 447 bytes .../auto/network/ssl/qsslkey/keys/dsa-pri-1024.pem | 12 + .../auto/network/ssl/qsslkey/keys/dsa-pri-512.der | Bin 0 -> 251 bytes .../auto/network/ssl/qsslkey/keys/dsa-pri-512.pem | 8 + .../auto/network/ssl/qsslkey/keys/dsa-pri-576.der | Bin 0 -> 275 bytes .../auto/network/ssl/qsslkey/keys/dsa-pri-576.pem | 8 + .../auto/network/ssl/qsslkey/keys/dsa-pri-960.der | Bin 0 -> 419 bytes .../auto/network/ssl/qsslkey/keys/dsa-pri-960.pem | 11 + .../auto/network/ssl/qsslkey/keys/dsa-pub-1024.der | Bin 0 -> 442 bytes .../auto/network/ssl/qsslkey/keys/dsa-pub-1024.pem | 12 + .../auto/network/ssl/qsslkey/keys/dsa-pub-512.der | Bin 0 -> 244 bytes .../auto/network/ssl/qsslkey/keys/dsa-pub-512.pem | 8 + .../auto/network/ssl/qsslkey/keys/dsa-pub-576.der | Bin 0 -> 268 bytes .../auto/network/ssl/qsslkey/keys/dsa-pub-576.pem | 8 + .../auto/network/ssl/qsslkey/keys/dsa-pub-960.der | Bin 0 -> 414 bytes .../auto/network/ssl/qsslkey/keys/dsa-pub-960.pem | 11 + tests/auto/network/ssl/qsslkey/keys/genkeys.sh | 82 + .../auto/network/ssl/qsslkey/keys/rsa-pri-1023.der | Bin 0 -> 605 bytes .../auto/network/ssl/qsslkey/keys/rsa-pri-1023.pem | 15 + .../auto/network/ssl/qsslkey/keys/rsa-pri-1024.der | Bin 0 -> 608 bytes .../auto/network/ssl/qsslkey/keys/rsa-pri-1024.pem | 15 + .../auto/network/ssl/qsslkey/keys/rsa-pri-2048.der | Bin 0 -> 1190 bytes .../auto/network/ssl/qsslkey/keys/rsa-pri-2048.pem | 27 + tests/auto/network/ssl/qsslkey/keys/rsa-pri-40.der | Bin 0 -> 49 bytes tests/auto/network/ssl/qsslkey/keys/rsa-pri-40.pem | 4 + .../auto/network/ssl/qsslkey/keys/rsa-pri-511.der | Bin 0 -> 316 bytes .../auto/network/ssl/qsslkey/keys/rsa-pri-511.pem | 9 + .../auto/network/ssl/qsslkey/keys/rsa-pri-512.der | Bin 0 -> 320 bytes .../auto/network/ssl/qsslkey/keys/rsa-pri-512.pem | 9 + .../auto/network/ssl/qsslkey/keys/rsa-pri-999.der | Bin 0 -> 591 bytes .../auto/network/ssl/qsslkey/keys/rsa-pri-999.pem | 15 + .../auto/network/ssl/qsslkey/keys/rsa-pub-1023.der | Bin 0 -> 161 bytes .../auto/network/ssl/qsslkey/keys/rsa-pub-1023.pem | 6 + .../auto/network/ssl/qsslkey/keys/rsa-pub-1024.der | Bin 0 -> 162 bytes .../auto/network/ssl/qsslkey/keys/rsa-pub-1024.pem | 6 + .../auto/network/ssl/qsslkey/keys/rsa-pub-2048.der | Bin 0 -> 294 bytes .../auto/network/ssl/qsslkey/keys/rsa-pub-2048.pem | 9 + tests/auto/network/ssl/qsslkey/keys/rsa-pub-40.der | Bin 0 -> 35 bytes tests/auto/network/ssl/qsslkey/keys/rsa-pub-40.pem | 3 + .../auto/network/ssl/qsslkey/keys/rsa-pub-511.der | Bin 0 -> 93 bytes .../auto/network/ssl/qsslkey/keys/rsa-pub-511.pem | 4 + .../auto/network/ssl/qsslkey/keys/rsa-pub-512.der | Bin 0 -> 94 bytes .../auto/network/ssl/qsslkey/keys/rsa-pub-512.pem | 4 + .../auto/network/ssl/qsslkey/keys/rsa-pub-999.der | Bin 0 -> 157 bytes .../auto/network/ssl/qsslkey/keys/rsa-pub-999.pem | 6 + tests/auto/network/ssl/qsslkey/qsslkey.pro | 32 + .../network/ssl/qsslkey/rsa-with-passphrase.pem | 18 + .../network/ssl/qsslkey/rsa-without-passphrase.pem | 15 + tests/auto/network/ssl/qsslkey/tst_qsslkey.cpp | 447 + tests/auto/network/ssl/qsslsocket/.gitignore | 1 + .../network/ssl/qsslsocket/certs/aspiriniks.ca.crt | 22 + .../ssl/qsslsocket/certs/fake-login.live.com.key | 15 + .../ssl/qsslsocket/certs/fake-login.live.com.pem | 19 + tests/auto/network/ssl/qsslsocket/certs/fluke.cert | 75 + tests/auto/network/ssl/qsslsocket/certs/fluke.key | 15 + .../ssl/qsslsocket/certs/qt-test-server-cacert.pem | 17 + tests/auto/network/ssl/qsslsocket/qsslsocket.pro | 48 + tests/auto/network/ssl/qsslsocket/ssl.tar.gz | Bin 0 -> 36299 bytes .../auto/network/ssl/qsslsocket/tst_qsslsocket.cpp | 2097 +++ tests/auto/network/ssl/ssl.pro | 8 + 280 files changed, 59668 insertions(+) create mode 100644 tests/auto/network/access/access.pro create mode 100644 tests/auto/network/access/qabstractnetworkcache/.gitignore create mode 100644 tests/auto/network/access/qabstractnetworkcache/qabstractnetworkcache.pro create mode 100755 tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_cachecontrol-expire.cgi create mode 100755 tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_cachecontrol.cgi create mode 100755 tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_cachecontrol200.cgi create mode 100755 tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_etag200.cgi create mode 100755 tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_etag304.cgi create mode 100755 tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_expires200.cgi create mode 100755 tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_expires304.cgi create mode 100755 tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_expires500.cgi create mode 100755 tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_lastModified200.cgi create mode 100755 tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_lastModified304.cgi create mode 100644 tests/auto/network/access/qabstractnetworkcache/tst_qabstractnetworkcache.cpp create mode 100644 tests/auto/network/access/qftp/.gitattributes create mode 100644 tests/auto/network/access/qftp/.gitignore create mode 100644 tests/auto/network/access/qftp/qftp.pro create mode 100644 tests/auto/network/access/qftp/rfc3252.txt create mode 100644 tests/auto/network/access/qftp/tst_qftp.cpp create mode 100644 tests/auto/network/access/qhttp/.gitattributes create mode 100644 tests/auto/network/access/qhttp/.gitignore create mode 100644 tests/auto/network/access/qhttp/dummyserver.h create mode 100644 tests/auto/network/access/qhttp/qhttp.pro create mode 100644 tests/auto/network/access/qhttp/rfc3252.txt create mode 100644 tests/auto/network/access/qhttp/trolltech create mode 100644 tests/auto/network/access/qhttp/tst_qhttp.cpp create mode 100755 tests/auto/network/access/qhttp/webserver/cgi-bin/retrieve_testfile.cgi create mode 100755 tests/auto/network/access/qhttp/webserver/cgi-bin/rfc.cgi create mode 100755 tests/auto/network/access/qhttp/webserver/cgi-bin/store_testfile.cgi create mode 100644 tests/auto/network/access/qhttp/webserver/index.html create mode 100644 tests/auto/network/access/qhttp/webserver/rfc3252 create mode 100644 tests/auto/network/access/qhttp/webserver/rfc3252.txt create mode 100644 tests/auto/network/access/qhttpnetworkconnection/.gitignore create mode 100644 tests/auto/network/access/qhttpnetworkconnection/qhttpnetworkconnection.pro create mode 100644 tests/auto/network/access/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp create mode 100644 tests/auto/network/access/qhttpnetworkreply/.gitignore create mode 100644 tests/auto/network/access/qhttpnetworkreply/qhttpnetworkreply.pro create mode 100644 tests/auto/network/access/qhttpnetworkreply/tst_qhttpnetworkreply.cpp create mode 100644 tests/auto/network/access/qnetworkaccessmanager/qnetworkaccessmanager.pro create mode 100644 tests/auto/network/access/qnetworkaccessmanager/tst_qnetworkaccessmanager.cpp create mode 100644 tests/auto/network/access/qnetworkcachemetadata/.gitignore create mode 100644 tests/auto/network/access/qnetworkcachemetadata/qnetworkcachemetadata.pro create mode 100644 tests/auto/network/access/qnetworkcachemetadata/tst_qnetworkcachemetadata.cpp create mode 100644 tests/auto/network/access/qnetworkcookie/.gitignore create mode 100644 tests/auto/network/access/qnetworkcookie/qnetworkcookie.pro create mode 100644 tests/auto/network/access/qnetworkcookie/tst_qnetworkcookie.cpp create mode 100644 tests/auto/network/access/qnetworkcookiejar/.gitignore create mode 100644 tests/auto/network/access/qnetworkcookiejar/qnetworkcookiejar.pro create mode 100644 tests/auto/network/access/qnetworkcookiejar/tst_qnetworkcookiejar.cpp create mode 100644 tests/auto/network/access/qnetworkdiskcache/.gitignore create mode 100644 tests/auto/network/access/qnetworkdiskcache/qnetworkdiskcache.pro create mode 100644 tests/auto/network/access/qnetworkdiskcache/tst_qnetworkdiskcache.cpp create mode 100644 tests/auto/network/access/qnetworkreply/.gitattributes create mode 100644 tests/auto/network/access/qnetworkreply/.gitignore create mode 100644 tests/auto/network/access/qnetworkreply/bigfile create mode 100644 tests/auto/network/access/qnetworkreply/certs/aspiriniks.ca.crt create mode 100644 tests/auto/network/access/qnetworkreply/certs/fluke.cert create mode 100644 tests/auto/network/access/qnetworkreply/certs/fluke.key create mode 100644 tests/auto/network/access/qnetworkreply/certs/qt-test-server-cacert.pem create mode 100644 tests/auto/network/access/qnetworkreply/certs/server.key create mode 100644 tests/auto/network/access/qnetworkreply/certs/server.pem create mode 100644 tests/auto/network/access/qnetworkreply/echo/echo.pro create mode 100644 tests/auto/network/access/qnetworkreply/echo/main.cpp create mode 100644 tests/auto/network/access/qnetworkreply/empty create mode 100644 tests/auto/network/access/qnetworkreply/image1.jpg create mode 100644 tests/auto/network/access/qnetworkreply/image2.jpg create mode 100644 tests/auto/network/access/qnetworkreply/image3.jpg create mode 100644 tests/auto/network/access/qnetworkreply/qnetworkreply.pro create mode 100644 tests/auto/network/access/qnetworkreply/qnetworkreply.qrc create mode 100644 tests/auto/network/access/qnetworkreply/resource create mode 100644 tests/auto/network/access/qnetworkreply/rfc3252.txt create mode 100644 tests/auto/network/access/qnetworkreply/smb-file.txt create mode 100644 tests/auto/network/access/qnetworkreply/test/test.pro create mode 100644 tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp create mode 100644 tests/auto/network/access/qnetworkrequest/.gitignore create mode 100644 tests/auto/network/access/qnetworkrequest/qnetworkrequest.pro create mode 100644 tests/auto/network/access/qnetworkrequest/tst_qnetworkrequest.cpp create mode 100644 tests/auto/network/bearer/bearer.pro create mode 100644 tests/auto/network/bearer/qbearertestcommon.h create mode 100644 tests/auto/network/bearer/qnetworkconfiguration/qnetworkconfiguration.pro create mode 100644 tests/auto/network/bearer/qnetworkconfiguration/tst_qnetworkconfiguration.cpp create mode 100644 tests/auto/network/bearer/qnetworkconfigurationmanager/qnetworkconfigurationmanager.pro create mode 100644 tests/auto/network/bearer/qnetworkconfigurationmanager/tst_qnetworkconfigurationmanager.cpp create mode 100644 tests/auto/network/bearer/qnetworksession/lackey/lackey.pro create mode 100644 tests/auto/network/bearer/qnetworksession/lackey/main.cpp create mode 100644 tests/auto/network/bearer/qnetworksession/qnetworksession.pro create mode 100644 tests/auto/network/bearer/qnetworksession/test/test.pro create mode 100644 tests/auto/network/bearer/qnetworksession/test/tst_qnetworksession.cpp create mode 100644 tests/auto/network/kernel/kernel.pro create mode 100644 tests/auto/network/kernel/qauthenticator/qauthenticator.pro create mode 100644 tests/auto/network/kernel/qauthenticator/tst_qauthenticator.cpp create mode 100644 tests/auto/network/kernel/qhostaddress/.gitignore create mode 100644 tests/auto/network/kernel/qhostaddress/qhostaddress.pro create mode 100644 tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp create mode 100644 tests/auto/network/kernel/qhostinfo/.gitignore create mode 100644 tests/auto/network/kernel/qhostinfo/qhostinfo.pro create mode 100644 tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp create mode 100644 tests/auto/network/kernel/qnetworkaddressentry/.gitignore create mode 100644 tests/auto/network/kernel/qnetworkaddressentry/qnetworkaddressentry.pro create mode 100644 tests/auto/network/kernel/qnetworkaddressentry/tst_qnetworkaddressentry.cpp create mode 100644 tests/auto/network/kernel/qnetworkinterface/.gitignore create mode 100644 tests/auto/network/kernel/qnetworkinterface/qnetworkinterface.pro create mode 100644 tests/auto/network/kernel/qnetworkinterface/tst_qnetworkinterface.cpp create mode 100644 tests/auto/network/kernel/qnetworkproxy/.gitignore create mode 100644 tests/auto/network/kernel/qnetworkproxy/qnetworkproxy.pro create mode 100644 tests/auto/network/kernel/qnetworkproxy/tst_qnetworkproxy.cpp create mode 100644 tests/auto/network/kernel/qnetworkproxyfactory/.gitignore create mode 100644 tests/auto/network/kernel/qnetworkproxyfactory/qnetworkproxyfactory.pro create mode 100644 tests/auto/network/kernel/qnetworkproxyfactory/tst_qnetworkproxyfactory.cpp create mode 100644 tests/auto/network/network.pro create mode 100644 tests/auto/network/socket/platformsocketengine/.gitignore create mode 100644 tests/auto/network/socket/platformsocketengine/platformsocketengine.pri create mode 100644 tests/auto/network/socket/platformsocketengine/platformsocketengine.pro create mode 100644 tests/auto/network/socket/platformsocketengine/tst_platformsocketengine.cpp create mode 100644 tests/auto/network/socket/qabstractsocket/.gitignore create mode 100644 tests/auto/network/socket/qabstractsocket/qabstractsocket.pro create mode 100644 tests/auto/network/socket/qabstractsocket/tst_qabstractsocket.cpp create mode 100644 tests/auto/network/socket/qhttpsocketengine/.gitignore create mode 100644 tests/auto/network/socket/qhttpsocketengine/qhttpsocketengine.pro create mode 100644 tests/auto/network/socket/qhttpsocketengine/tst_qhttpsocketengine.cpp create mode 100644 tests/auto/network/socket/qlocalsocket/.gitignore create mode 100644 tests/auto/network/socket/qlocalsocket/example/client/client.pro create mode 100644 tests/auto/network/socket/qlocalsocket/example/client/main.cpp create mode 100644 tests/auto/network/socket/qlocalsocket/example/example.pro create mode 100644 tests/auto/network/socket/qlocalsocket/example/server/main.cpp create mode 100644 tests/auto/network/socket/qlocalsocket/example/server/server.pro create mode 100644 tests/auto/network/socket/qlocalsocket/lackey/lackey.pro create mode 100644 tests/auto/network/socket/qlocalsocket/lackey/main.cpp create mode 100755 tests/auto/network/socket/qlocalsocket/lackey/scripts/client.js create mode 100644 tests/auto/network/socket/qlocalsocket/lackey/scripts/server.js create mode 100644 tests/auto/network/socket/qlocalsocket/qlocalsocket.pro create mode 100644 tests/auto/network/socket/qlocalsocket/test/test.pro create mode 100644 tests/auto/network/socket/qlocalsocket/tst_qlocalsocket.cpp create mode 100644 tests/auto/network/socket/qsocks5socketengine/.gitignore create mode 100644 tests/auto/network/socket/qsocks5socketengine/qsocks5socketengine.pro create mode 100644 tests/auto/network/socket/qsocks5socketengine/tst_qsocks5socketengine.cpp create mode 100644 tests/auto/network/socket/qtcpserver/.gitignore create mode 100644 tests/auto/network/socket/qtcpserver/crashingServer/crashingServer.pro create mode 100644 tests/auto/network/socket/qtcpserver/crashingServer/main.cpp create mode 100644 tests/auto/network/socket/qtcpserver/qtcpserver.pro create mode 100644 tests/auto/network/socket/qtcpserver/test/test.pro create mode 100644 tests/auto/network/socket/qtcpserver/tst_qtcpserver.cpp create mode 100644 tests/auto/network/socket/qtcpsocket/.gitignore create mode 100644 tests/auto/network/socket/qtcpsocket/qtcpsocket.pro create mode 100644 tests/auto/network/socket/qtcpsocket/stressTest/Test.cpp create mode 100644 tests/auto/network/socket/qtcpsocket/stressTest/Test.h create mode 100644 tests/auto/network/socket/qtcpsocket/stressTest/main.cpp create mode 100644 tests/auto/network/socket/qtcpsocket/stressTest/stressTest.pro create mode 100644 tests/auto/network/socket/qtcpsocket/test/test.pro create mode 100644 tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp create mode 100644 tests/auto/network/socket/qudpsocket/.gitignore create mode 100644 tests/auto/network/socket/qudpsocket/clientserver/clientserver.pro create mode 100644 tests/auto/network/socket/qudpsocket/clientserver/main.cpp create mode 100644 tests/auto/network/socket/qudpsocket/qudpsocket.pro create mode 100644 tests/auto/network/socket/qudpsocket/test/test.pro create mode 100644 tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp create mode 100644 tests/auto/network/socket/qudpsocket/udpServer/main.cpp create mode 100644 tests/auto/network/socket/qudpsocket/udpServer/udpServer.pro create mode 100644 tests/auto/network/socket/socket.pro create mode 100644 tests/auto/network/ssl/qsslcertificate/.gitignore create mode 100644 tests/auto/network/ssl/qsslcertificate/certificates/ca-cert.pem create mode 100644 tests/auto/network/ssl/qsslcertificate/certificates/ca-cert.pem.digest-md5 create mode 100644 tests/auto/network/ssl/qsslcertificate/certificates/ca-cert.pem.digest-sha1 create mode 100644 tests/auto/network/ssl/qsslcertificate/certificates/cert-ss-san-utf8.pem create mode 100644 tests/auto/network/ssl/qsslcertificate/certificates/cert-ss-san-utf8.pem.san create mode 100644 tests/auto/network/ssl/qsslcertificate/certificates/cert-ss-san.pem create mode 100644 tests/auto/network/ssl/qsslcertificate/certificates/cert-ss-san.pem.san create mode 100644 tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.der create mode 100644 tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.der.pubkey create mode 100644 tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.pem create mode 100644 tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.pem.digest-md5 create mode 100644 tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.pem.digest-sha1 create mode 100644 tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.pem.pubkey create mode 100644 tests/auto/network/ssl/qsslcertificate/certificates/cert.der create mode 100644 tests/auto/network/ssl/qsslcertificate/certificates/cert.der.pubkey create mode 100644 tests/auto/network/ssl/qsslcertificate/certificates/cert.pem create mode 100644 tests/auto/network/ssl/qsslcertificate/certificates/cert.pem.digest-md5 create mode 100644 tests/auto/network/ssl/qsslcertificate/certificates/cert.pem.digest-sha1 create mode 100644 tests/auto/network/ssl/qsslcertificate/certificates/cert.pem.pubkey create mode 100755 tests/auto/network/ssl/qsslcertificate/certificates/gencertificates.sh create mode 100644 tests/auto/network/ssl/qsslcertificate/certificates/san.cnf create mode 100644 tests/auto/network/ssl/qsslcertificate/more-certificates/badguy-nul-cn.crt create mode 100644 tests/auto/network/ssl/qsslcertificate/more-certificates/badguy-nul-san.crt create mode 100644 tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted-google.com-diginotar.pem create mode 100644 tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted1.pem create mode 100644 tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted2.pem create mode 100644 tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted3.pem create mode 100644 tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted4.pem create mode 100644 tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted5.pem create mode 100644 tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted6.pem create mode 100644 tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted7.pem create mode 100644 tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted8.pem create mode 100644 tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted9.pem create mode 100644 tests/auto/network/ssl/qsslcertificate/more-certificates/cert-large-expiration-date.pem create mode 100644 tests/auto/network/ssl/qsslcertificate/more-certificates/cert-large-expiration-date.txt.0.9.8 create mode 100644 tests/auto/network/ssl/qsslcertificate/more-certificates/cert-large-expiration-date.txt.1.0.0 create mode 100644 tests/auto/network/ssl/qsslcertificate/more-certificates/cert-large-serial-number.pem create mode 100644 tests/auto/network/ssl/qsslcertificate/more-certificates/malformed-just-begin-no-newline.pem create mode 100644 tests/auto/network/ssl/qsslcertificate/more-certificates/malformed-just-begin.pem create mode 100644 tests/auto/network/ssl/qsslcertificate/more-certificates/natwest-banking.pem create mode 100644 tests/auto/network/ssl/qsslcertificate/more-certificates/no-ending-newline.pem create mode 100644 tests/auto/network/ssl/qsslcertificate/more-certificates/test-cn-two-cns-cert.pem create mode 100644 tests/auto/network/ssl/qsslcertificate/more-certificates/test-cn-with-drink-cert.pem create mode 100644 tests/auto/network/ssl/qsslcertificate/more-certificates/trailing-whitespace.pem create mode 100644 tests/auto/network/ssl/qsslcertificate/qsslcertificate.pro create mode 100644 tests/auto/network/ssl/qsslcertificate/tst_qsslcertificate.cpp create mode 100644 tests/auto/network/ssl/qsslcertificate/verify-certs/README create mode 100644 tests/auto/network/ssl/qsslcertificate/verify-certs/cacert.pem create mode 100644 tests/auto/network/ssl/qsslcertificate/verify-certs/test-addons-mozilla-org-cert.pem create mode 100644 tests/auto/network/ssl/qsslcertificate/verify-certs/test-intermediate-ca-cert.pem create mode 100644 tests/auto/network/ssl/qsslcertificate/verify-certs/test-intermediate-is-ca-cert.pem create mode 100644 tests/auto/network/ssl/qsslcertificate/verify-certs/test-intermediate-not-ca-cert.pem create mode 100644 tests/auto/network/ssl/qsslcertificate/verify-certs/test-ocsp-good-cert.pem create mode 100644 tests/auto/network/ssl/qsslcipher/.gitignore create mode 100644 tests/auto/network/ssl/qsslcipher/qsslcipher.pro create mode 100644 tests/auto/network/ssl/qsslcipher/tst_qsslcipher.cpp create mode 100644 tests/auto/network/ssl/qsslerror/.gitignore create mode 100644 tests/auto/network/ssl/qsslerror/qsslerror.pro create mode 100644 tests/auto/network/ssl/qsslerror/tst_qsslerror.cpp create mode 100644 tests/auto/network/ssl/qsslkey/.gitignore create mode 100644 tests/auto/network/ssl/qsslkey/keys/dsa-pri-1024.der create mode 100644 tests/auto/network/ssl/qsslkey/keys/dsa-pri-1024.pem create mode 100644 tests/auto/network/ssl/qsslkey/keys/dsa-pri-512.der create mode 100644 tests/auto/network/ssl/qsslkey/keys/dsa-pri-512.pem create mode 100644 tests/auto/network/ssl/qsslkey/keys/dsa-pri-576.der create mode 100644 tests/auto/network/ssl/qsslkey/keys/dsa-pri-576.pem create mode 100644 tests/auto/network/ssl/qsslkey/keys/dsa-pri-960.der create mode 100644 tests/auto/network/ssl/qsslkey/keys/dsa-pri-960.pem create mode 100644 tests/auto/network/ssl/qsslkey/keys/dsa-pub-1024.der create mode 100644 tests/auto/network/ssl/qsslkey/keys/dsa-pub-1024.pem create mode 100644 tests/auto/network/ssl/qsslkey/keys/dsa-pub-512.der create mode 100644 tests/auto/network/ssl/qsslkey/keys/dsa-pub-512.pem create mode 100644 tests/auto/network/ssl/qsslkey/keys/dsa-pub-576.der create mode 100644 tests/auto/network/ssl/qsslkey/keys/dsa-pub-576.pem create mode 100644 tests/auto/network/ssl/qsslkey/keys/dsa-pub-960.der create mode 100644 tests/auto/network/ssl/qsslkey/keys/dsa-pub-960.pem create mode 100755 tests/auto/network/ssl/qsslkey/keys/genkeys.sh create mode 100644 tests/auto/network/ssl/qsslkey/keys/rsa-pri-1023.der create mode 100644 tests/auto/network/ssl/qsslkey/keys/rsa-pri-1023.pem create mode 100644 tests/auto/network/ssl/qsslkey/keys/rsa-pri-1024.der create mode 100644 tests/auto/network/ssl/qsslkey/keys/rsa-pri-1024.pem create mode 100644 tests/auto/network/ssl/qsslkey/keys/rsa-pri-2048.der create mode 100644 tests/auto/network/ssl/qsslkey/keys/rsa-pri-2048.pem create mode 100644 tests/auto/network/ssl/qsslkey/keys/rsa-pri-40.der create mode 100644 tests/auto/network/ssl/qsslkey/keys/rsa-pri-40.pem create mode 100644 tests/auto/network/ssl/qsslkey/keys/rsa-pri-511.der create mode 100644 tests/auto/network/ssl/qsslkey/keys/rsa-pri-511.pem create mode 100644 tests/auto/network/ssl/qsslkey/keys/rsa-pri-512.der create mode 100644 tests/auto/network/ssl/qsslkey/keys/rsa-pri-512.pem create mode 100644 tests/auto/network/ssl/qsslkey/keys/rsa-pri-999.der create mode 100644 tests/auto/network/ssl/qsslkey/keys/rsa-pri-999.pem create mode 100644 tests/auto/network/ssl/qsslkey/keys/rsa-pub-1023.der create mode 100644 tests/auto/network/ssl/qsslkey/keys/rsa-pub-1023.pem create mode 100644 tests/auto/network/ssl/qsslkey/keys/rsa-pub-1024.der create mode 100644 tests/auto/network/ssl/qsslkey/keys/rsa-pub-1024.pem create mode 100644 tests/auto/network/ssl/qsslkey/keys/rsa-pub-2048.der create mode 100644 tests/auto/network/ssl/qsslkey/keys/rsa-pub-2048.pem create mode 100644 tests/auto/network/ssl/qsslkey/keys/rsa-pub-40.der create mode 100644 tests/auto/network/ssl/qsslkey/keys/rsa-pub-40.pem create mode 100644 tests/auto/network/ssl/qsslkey/keys/rsa-pub-511.der create mode 100644 tests/auto/network/ssl/qsslkey/keys/rsa-pub-511.pem create mode 100644 tests/auto/network/ssl/qsslkey/keys/rsa-pub-512.der create mode 100644 tests/auto/network/ssl/qsslkey/keys/rsa-pub-512.pem create mode 100644 tests/auto/network/ssl/qsslkey/keys/rsa-pub-999.der create mode 100644 tests/auto/network/ssl/qsslkey/keys/rsa-pub-999.pem create mode 100644 tests/auto/network/ssl/qsslkey/qsslkey.pro create mode 100644 tests/auto/network/ssl/qsslkey/rsa-with-passphrase.pem create mode 100644 tests/auto/network/ssl/qsslkey/rsa-without-passphrase.pem create mode 100644 tests/auto/network/ssl/qsslkey/tst_qsslkey.cpp create mode 100644 tests/auto/network/ssl/qsslsocket/.gitignore create mode 100644 tests/auto/network/ssl/qsslsocket/certs/aspiriniks.ca.crt create mode 100644 tests/auto/network/ssl/qsslsocket/certs/fake-login.live.com.key create mode 100644 tests/auto/network/ssl/qsslsocket/certs/fake-login.live.com.pem create mode 100644 tests/auto/network/ssl/qsslsocket/certs/fluke.cert create mode 100644 tests/auto/network/ssl/qsslsocket/certs/fluke.key create mode 100644 tests/auto/network/ssl/qsslsocket/certs/qt-test-server-cacert.pem create mode 100644 tests/auto/network/ssl/qsslsocket/qsslsocket.pro create mode 100644 tests/auto/network/ssl/qsslsocket/ssl.tar.gz create mode 100644 tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp create mode 100644 tests/auto/network/ssl/ssl.pro (limited to 'tests/auto/network') diff --git a/tests/auto/network/access/access.pro b/tests/auto/network/access/access.pro new file mode 100644 index 0000000000..53b16f07b8 --- /dev/null +++ b/tests/auto/network/access/access.pro @@ -0,0 +1,20 @@ +TEMPLATE=subdirs +SUBDIRS=\ + qnetworkdiskcache \ + qnetworkcookiejar \ + qnetworkaccessmanager \ + qnetworkcookie \ + qnetworkrequest \ + qhttpnetworkconnection \ + qnetworkreply \ + qnetworkcachemetadata \ + qftp \ + qhttpnetworkreply \ + qhttp \ + qabstractnetworkcache \ + +!contains(QT_CONFIG, private_tests): SUBDIRS -= \ + qhttpnetworkconnection \ + qhttpnetworkreply \ + + diff --git a/tests/auto/network/access/qabstractnetworkcache/.gitignore b/tests/auto/network/access/qabstractnetworkcache/.gitignore new file mode 100644 index 0000000000..72766dddce --- /dev/null +++ b/tests/auto/network/access/qabstractnetworkcache/.gitignore @@ -0,0 +1 @@ +tst_qabstractnetworkcache diff --git a/tests/auto/network/access/qabstractnetworkcache/qabstractnetworkcache.pro b/tests/auto/network/access/qabstractnetworkcache/qabstractnetworkcache.pro new file mode 100644 index 0000000000..4dea3c9c8c --- /dev/null +++ b/tests/auto/network/access/qabstractnetworkcache/qabstractnetworkcache.pro @@ -0,0 +1,14 @@ +load(qttest_p4) +QT += network +QT -= gui +SOURCES += tst_qabstractnetworkcache.cpp + +wince*|symbian: { + testFiles.files = tests + testFiles.path = . + DEPLOYMENT += testFiles +} + +symbian: TARGET.CAPABILITY = NetworkServices + +CONFIG += insignificant_test # QTBUG-20686; note, assumed unstable on all platforms diff --git a/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_cachecontrol-expire.cgi b/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_cachecontrol-expire.cgi new file mode 100755 index 0000000000..7dc506fc1e --- /dev/null +++ b/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_cachecontrol-expire.cgi @@ -0,0 +1,7 @@ +#!/bin/bash +# cache control takes precedence over expires +echo "Cache-Control: max-age=-1" +echo "Expires: Mon, 30 Oct 2028 14:19:41 GMT" +echo "Content-type: text/html"; +echo "" +echo "Hello World!" diff --git a/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_cachecontrol.cgi b/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_cachecontrol.cgi new file mode 100755 index 0000000000..f2edfc161f --- /dev/null +++ b/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_cachecontrol.cgi @@ -0,0 +1,13 @@ +#!/bin/bash +if [ ! -z ${HTTP_IF_MODIFIED_SINCE} ] ; then + echo "Status: 304" + echo "" + exit; +fi + +cc=`echo "${QUERY_STRING}" | sed -e s/%20/\ /g` +echo "Cache-Control: $cc" +echo "Last-Modified: Sat, 31 Oct 1981 06:00:00 GMT" +echo "Content-type: text/html"; +echo "" +echo "Hello World!" diff --git a/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_cachecontrol200.cgi b/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_cachecontrol200.cgi new file mode 100755 index 0000000000..e44d5ed570 --- /dev/null +++ b/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_cachecontrol200.cgi @@ -0,0 +1,9 @@ +#!/bin/bash +cc=`echo "${QUERY_STRING}" | sed -e s/%20/\ /g` +echo "Status: 200" +echo "Cache-Control: $cc" +echo "Last-Modified: Sat, 31 Oct 1981 06:00:00 GMT" +echo "Content-type: text/html"; +echo "X-Script: $0" +echo "" +echo "Hello World!" diff --git a/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_etag200.cgi b/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_etag200.cgi new file mode 100755 index 0000000000..0966abfdd1 --- /dev/null +++ b/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_etag200.cgi @@ -0,0 +1,5 @@ +#!/bin/bash +echo "ETag: foo" +echo "Content-type: text/html"; +echo "" +echo "Hello World!" diff --git a/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_etag304.cgi b/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_etag304.cgi new file mode 100755 index 0000000000..91a4b922bd --- /dev/null +++ b/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_etag304.cgi @@ -0,0 +1,11 @@ +#!/bin/bash +if [ ! -z ${HTTP_IF_NONE_MATCH} ] ; then + echo "Status: 304" + echo "" + exit; +fi + +echo "ETag: foo" +echo "Content-type: text/html"; +echo "" +echo "Hello World!" diff --git a/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_expires200.cgi b/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_expires200.cgi new file mode 100755 index 0000000000..e18ebc86ad --- /dev/null +++ b/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_expires200.cgi @@ -0,0 +1,5 @@ +#!/bin/bash +echo "Expires: Sat, 31 Oct 1981 6:00:00 GMT" +echo "Content-type: text/html"; +echo "" +echo "Hello World!" diff --git a/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_expires304.cgi b/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_expires304.cgi new file mode 100755 index 0000000000..1c7de1cd77 --- /dev/null +++ b/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_expires304.cgi @@ -0,0 +1,11 @@ +#!/bin/bash +if [ ${HTTP_IF_MODIFIED_SINCE} == "Mon, 30 Oct 2028 14:19:41 GMT" ] ; then + echo "Status: 304" + echo "" + exit; +fi + +echo "Expires: Mon, 30 Oct 2028 14:19:41 GMT" +echo "Content-type: text/html"; +echo "" +echo "Hello World!" diff --git a/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_expires500.cgi b/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_expires500.cgi new file mode 100755 index 0000000000..9615c4f0bd --- /dev/null +++ b/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_expires500.cgi @@ -0,0 +1,11 @@ +#!/bin/bash +if [ ! -z ${HTTP_IF_MODIFIED_SINCE} ] ; then + echo "Status: 500" + echo "" + exit; +fi + +echo "Expires: Mon, 30 Oct 2028 14:19:41 GMT" +echo "Content-type: text/html"; +echo "" +echo "Hello World!" diff --git a/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_lastModified200.cgi b/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_lastModified200.cgi new file mode 100755 index 0000000000..5dc219b1e7 --- /dev/null +++ b/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_lastModified200.cgi @@ -0,0 +1,5 @@ +#!/bin/bash +echo "Last-Modified: Sat, 31 Oct 1981 6:00:00 GMT" +echo "Content-type: text/html"; +echo "" +echo "Hello World!" diff --git a/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_lastModified304.cgi b/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_lastModified304.cgi new file mode 100755 index 0000000000..bdf23bed2f --- /dev/null +++ b/tests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_lastModified304.cgi @@ -0,0 +1,11 @@ +#!/bin/bash +if [ ${HTTP_IF_MODIFIED_SINCE} == "Sat, 31 Oct 1981 06:00:00 GMT" ] ; then + echo "Status: 304" + echo "" + exit; +fi + +echo "Last-Modified: Sat, 31 Oct 1981 06:00:00 GMT" +echo "Content-type: text/html"; +echo "" +echo "Hello World!" diff --git a/tests/auto/network/access/qabstractnetworkcache/tst_qabstractnetworkcache.cpp b/tests/auto/network/access/qabstractnetworkcache/tst_qabstractnetworkcache.cpp new file mode 100644 index 0000000000..244c9b13b5 --- /dev/null +++ b/tests/auto/network/access/qabstractnetworkcache/tst_qabstractnetworkcache.cpp @@ -0,0 +1,416 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include +#include "../../../../shared/util.h" +#include "../../../network-settings.h" + +#ifndef QT_NO_BEARERMANAGEMENT +#include +#include +#include +#endif + +#define TESTFILE QString("http://%1/qtest/cgi-bin/").arg(QtNetworkSettings::serverName()) + +class tst_QAbstractNetworkCache : public QObject +{ + Q_OBJECT + +public: + tst_QAbstractNetworkCache(); + virtual ~tst_QAbstractNetworkCache(); + +private slots: + void initTestCase(); + void expires_data(); + void expires(); + void expiresSynchronous_data(); + void expiresSynchronous(); + + void lastModified_data(); + void lastModified(); + void lastModifiedSynchronous_data(); + void lastModifiedSynchronous(); + + void etag_data(); + void etag(); + void etagSynchronous_data(); + void etagSynchronous(); + + void cacheControl_data(); + void cacheControl(); + void cacheControlSynchronous_data(); + void cacheControlSynchronous(); + + void deleteCache(); + +private: + void check(); + void checkSynchronous(); + +#ifndef QT_NO_BEARERMANAGEMENT + QNetworkConfigurationManager *netConfMan; + QNetworkConfiguration networkConfiguration; + QScopedPointer networkSession; +#endif +}; + +class NetworkDiskCache : public QNetworkDiskCache +{ + Q_OBJECT +public: + NetworkDiskCache(QObject *parent = 0) + : QNetworkDiskCache(parent) + , gotData(false) + { + QString location = QDir::tempPath() + QLatin1String("/tst_qnetworkdiskcache/"); + setCacheDirectory(location); + clear(); + } + + QIODevice *data(const QUrl &url) + { + gotData = true; + return QNetworkDiskCache::data(url); + } + + bool gotData; +}; + + +tst_QAbstractNetworkCache::tst_QAbstractNetworkCache() +{ + Q_SET_DEFAULT_IAP + + QCoreApplication::setOrganizationName(QLatin1String("Trolltech")); + QCoreApplication::setApplicationName(QLatin1String("autotest_qabstractnetworkcache")); + QCoreApplication::setApplicationVersion(QLatin1String("1.0")); +} + +tst_QAbstractNetworkCache::~tst_QAbstractNetworkCache() +{ +} + +static bool AlwaysTrue = true; +static bool AlwaysFalse = false; + +Q_DECLARE_METATYPE(QNetworkRequest::CacheLoadControl) + + +void tst_QAbstractNetworkCache::initTestCase() +{ +#ifndef QT_NO_BEARERMANAGEMENT + netConfMan = new QNetworkConfigurationManager(this); + networkConfiguration = netConfMan->defaultConfiguration(); + networkSession.reset(new QNetworkSession(networkConfiguration)); + if (!networkSession->isOpen()) { + networkSession->open(); + QVERIFY(networkSession->waitForOpened(30000)); + } +#endif +} + + +void tst_QAbstractNetworkCache::expires_data() +{ + QTest::addColumn("cacheLoadControl"); + QTest::addColumn("url"); + QTest::addColumn("fetchFromCache"); + + QTest::newRow("304-0") << QNetworkRequest::AlwaysNetwork << "httpcachetest_expires304.cgi" << AlwaysFalse; + QTest::newRow("304-1") << QNetworkRequest::PreferNetwork << "httpcachetest_expires304.cgi" << true; + QTest::newRow("304-2") << QNetworkRequest::AlwaysCache << "httpcachetest_expires304.cgi" << AlwaysTrue; + QTest::newRow("304-3") << QNetworkRequest::PreferCache << "httpcachetest_expires304.cgi" << true; + + QTest::newRow("500-0") << QNetworkRequest::AlwaysNetwork << "httpcachetest_expires500.cgi" << AlwaysFalse; + QTest::newRow("500-1") << QNetworkRequest::PreferNetwork << "httpcachetest_expires500.cgi" << true; + QTest::newRow("500-2") << QNetworkRequest::AlwaysCache << "httpcachetest_expires500.cgi" << AlwaysTrue; + QTest::newRow("500-3") << QNetworkRequest::PreferCache << "httpcachetest_expires500.cgi" << true; + + QTest::newRow("200-0") << QNetworkRequest::AlwaysNetwork << "httpcachetest_expires200.cgi" << AlwaysFalse; + QTest::newRow("200-1") << QNetworkRequest::PreferNetwork << "httpcachetest_expires200.cgi" << false; + QTest::newRow("200-2") << QNetworkRequest::AlwaysCache << "httpcachetest_expires200.cgi" << AlwaysTrue; + QTest::newRow("200-3") << QNetworkRequest::PreferCache << "httpcachetest_expires200.cgi" << false; +} + +void tst_QAbstractNetworkCache::expires() +{ + check(); +} + +void tst_QAbstractNetworkCache::expiresSynchronous_data() +{ + expires_data(); +} + +void tst_QAbstractNetworkCache::expiresSynchronous() +{ + checkSynchronous(); +} + +void tst_QAbstractNetworkCache::lastModified_data() +{ + QTest::addColumn("cacheLoadControl"); + QTest::addColumn("url"); + QTest::addColumn("fetchFromCache"); + + QTest::newRow("304-0") << QNetworkRequest::AlwaysNetwork << "httpcachetest_lastModified304.cgi" << AlwaysFalse; + QTest::newRow("304-1") << QNetworkRequest::PreferNetwork << "httpcachetest_lastModified304.cgi" << true; + QTest::newRow("304-2") << QNetworkRequest::AlwaysCache << "httpcachetest_lastModified304.cgi" << AlwaysTrue; + QTest::newRow("304-3") << QNetworkRequest::PreferCache << "httpcachetest_lastModified304.cgi" << true; + + QTest::newRow("200-0") << QNetworkRequest::AlwaysNetwork << "httpcachetest_lastModified200.cgi" << AlwaysFalse; + QTest::newRow("200-1") << QNetworkRequest::PreferNetwork << "httpcachetest_lastModified200.cgi" << false; + QTest::newRow("200-2") << QNetworkRequest::AlwaysCache << "httpcachetest_lastModified200.cgi" << AlwaysTrue; + QTest::newRow("200-3") << QNetworkRequest::PreferCache << "httpcachetest_lastModified200.cgi" << false; +} + +void tst_QAbstractNetworkCache::lastModified() +{ + check(); +} + +void tst_QAbstractNetworkCache::lastModifiedSynchronous_data() +{ + tst_QAbstractNetworkCache::lastModified_data(); +} + +void tst_QAbstractNetworkCache::lastModifiedSynchronous() +{ + checkSynchronous(); +} + +void tst_QAbstractNetworkCache::etag_data() +{ + QTest::addColumn("cacheLoadControl"); + QTest::addColumn("url"); + QTest::addColumn("fetchFromCache"); + + QTest::newRow("304-0") << QNetworkRequest::AlwaysNetwork << "httpcachetest_etag304.cgi" << AlwaysFalse; + QTest::newRow("304-1") << QNetworkRequest::PreferNetwork << "httpcachetest_etag304.cgi" << true; + QTest::newRow("304-2") << QNetworkRequest::AlwaysCache << "httpcachetest_etag304.cgi" << AlwaysTrue; + QTest::newRow("304-3") << QNetworkRequest::PreferCache << "httpcachetest_etag304.cgi" << true; + + QTest::newRow("200-0") << QNetworkRequest::AlwaysNetwork << "httpcachetest_etag200.cgi" << AlwaysFalse; + QTest::newRow("200-1") << QNetworkRequest::PreferNetwork << "httpcachetest_etag200.cgi" << false; + QTest::newRow("200-2") << QNetworkRequest::AlwaysCache << "httpcachetest_etag200.cgi" << AlwaysTrue; + QTest::newRow("200-3") << QNetworkRequest::PreferCache << "httpcachetest_etag200.cgi" << false; +} + +void tst_QAbstractNetworkCache::etag() +{ + check(); +} + +void tst_QAbstractNetworkCache::etagSynchronous_data() +{ + tst_QAbstractNetworkCache::etag_data(); +} + +void tst_QAbstractNetworkCache::etagSynchronous() +{ + checkSynchronous(); +} + +void tst_QAbstractNetworkCache::cacheControl_data() +{ + QTest::addColumn("cacheLoadControl"); + QTest::addColumn("url"); + QTest::addColumn("fetchFromCache"); + QTest::newRow("200-0") << QNetworkRequest::PreferNetwork << "httpcachetest_cachecontrol.cgi?max-age=-1" << true; + QTest::newRow("200-1") << QNetworkRequest::PreferNetwork << "httpcachetest_cachecontrol-expire.cgi" << false; + + QTest::newRow("200-2") << QNetworkRequest::AlwaysNetwork << "httpcachetest_cachecontrol.cgi?no-cache" << AlwaysFalse; + QTest::newRow("200-3") << QNetworkRequest::PreferNetwork << "httpcachetest_cachecontrol.cgi?no-cache" << false; + QTest::newRow("200-4") << QNetworkRequest::AlwaysCache << "httpcachetest_cachecontrol.cgi?no-cache" << false; + QTest::newRow("200-5") << QNetworkRequest::PreferCache << "httpcachetest_cachecontrol.cgi?no-cache" << false; + + QTest::newRow("304-0") << QNetworkRequest::PreferNetwork << "httpcachetest_cachecontrol.cgi?max-age=1000" << true; + + QTest::newRow("304-1") << QNetworkRequest::AlwaysNetwork << "httpcachetest_cachecontrol.cgi?max-age=1000, must-revalidate" << AlwaysFalse; + QTest::newRow("304-2") << QNetworkRequest::PreferNetwork << "httpcachetest_cachecontrol.cgi?max-age=1000, must-revalidate" << true; + QTest::newRow("304-3") << QNetworkRequest::AlwaysCache << "httpcachetest_cachecontrol.cgi?max-age=1000, must-revalidate" << false; + QTest::newRow("304-4") << QNetworkRequest::PreferCache << "httpcachetest_cachecontrol.cgi?max-age=1000, must-revalidate" << true; + + // see QTBUG-7060 + //QTest::newRow("nokia-boston") << QNetworkRequest::PreferNetwork << "http://waplabdc.nokia-boston.com/browser/users/venkat/cache/Cache_directives/private_1b.asp" << true; + QTest::newRow("304-2b") << QNetworkRequest::PreferNetwork << "httpcachetest_cachecontrol200.cgi?private, max-age=1000" << true; + QTest::newRow("304-4b") << QNetworkRequest::PreferCache << "httpcachetest_cachecontrol200.cgi?private, max-age=1000" << true; +} + +void tst_QAbstractNetworkCache::cacheControl() +{ + check(); +} + +void tst_QAbstractNetworkCache::cacheControlSynchronous_data() +{ + tst_QAbstractNetworkCache::cacheControl_data(); +} + +void tst_QAbstractNetworkCache::cacheControlSynchronous() +{ + checkSynchronous(); +} + +void tst_QAbstractNetworkCache::check() +{ + QFETCH(QNetworkRequest::CacheLoadControl, cacheLoadControl); + QFETCH(QString, url); + QFETCH(bool, fetchFromCache); + + QNetworkAccessManager manager; + NetworkDiskCache *diskCache = new NetworkDiskCache(&manager); + manager.setCache(diskCache); + QCOMPARE(diskCache->gotData, false); + + QUrl realUrl = url.contains("://") ? url : TESTFILE + url; + QNetworkRequest request(realUrl); + + // prime the cache + QNetworkReply *reply = manager.get(request); + QSignalSpy downloaded1(reply, SIGNAL(finished())); + QTRY_COMPARE(downloaded1.count(), 1); + QCOMPARE(diskCache->gotData, false); + QByteArray goodData = reply->readAll(); + + request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, cacheLoadControl); + + // should be in the cache now + QNetworkReply *reply2 = manager.get(request); + QSignalSpy downloaded2(reply2, SIGNAL(finished())); + QTRY_COMPARE(downloaded2.count(), 1); + + QByteArray secondData = reply2->readAll(); + if (!fetchFromCache && cacheLoadControl == QNetworkRequest::AlwaysCache) { + QCOMPARE(reply2->error(), QNetworkReply::ContentNotFoundError); + QCOMPARE(secondData, QByteArray()); + } else { + QCOMPARE(reply2->error(), QNetworkReply::NoError); + QCOMPARE(QString(secondData), QString(goodData)); + QCOMPARE(secondData, goodData); + QCOMPARE(reply2->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + } + + if (fetchFromCache) { + QList rawHeaderList = reply->rawHeaderList(); + QList rawHeaderList2 = reply2->rawHeaderList(); + qSort(rawHeaderList); + qSort(rawHeaderList2); + } + QCOMPARE(diskCache->gotData, fetchFromCache); +} + +void tst_QAbstractNetworkCache::checkSynchronous() +{ + QSKIP("not working yet, see QTBUG-15221", SkipAll); + + QFETCH(QNetworkRequest::CacheLoadControl, cacheLoadControl); + QFETCH(QString, url); + QFETCH(bool, fetchFromCache); + + QNetworkAccessManager manager; + NetworkDiskCache *diskCache = new NetworkDiskCache(&manager); + manager.setCache(diskCache); + QCOMPARE(diskCache->gotData, false); + + QUrl realUrl = url.contains("://") ? url : TESTFILE + url; + QNetworkRequest request(realUrl); + + request.setAttribute( + QNetworkRequest::SynchronousRequestAttribute, + true); + + // prime the cache + QNetworkReply *reply = manager.get(request); + QVERIFY(reply->isFinished()); // synchronous + QCOMPARE(diskCache->gotData, false); + QByteArray goodData = reply->readAll(); + + request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, cacheLoadControl); + + // should be in the cache now + QNetworkReply *reply2 = manager.get(request); + QVERIFY(reply2->isFinished()); // synchronous + + QByteArray secondData = reply2->readAll(); + if (!fetchFromCache && cacheLoadControl == QNetworkRequest::AlwaysCache) { + QCOMPARE(reply2->error(), QNetworkReply::ContentNotFoundError); + QCOMPARE(secondData, QByteArray()); + } else { + if (reply2->error() != QNetworkReply::NoError) + qDebug() << reply2->errorString(); + QCOMPARE(reply2->error(), QNetworkReply::NoError); + QCOMPARE(QString(secondData), QString(goodData)); + QCOMPARE(secondData, goodData); + QCOMPARE(reply2->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + } + + if (fetchFromCache) { + QList rawHeaderList = reply->rawHeaderList(); + QList rawHeaderList2 = reply2->rawHeaderList(); + qSort(rawHeaderList); + qSort(rawHeaderList2); + } + QCOMPARE(diskCache->gotData, fetchFromCache); +} + +void tst_QAbstractNetworkCache::deleteCache() +{ + QNetworkAccessManager manager; + NetworkDiskCache *diskCache = new NetworkDiskCache(&manager); + manager.setCache(diskCache); + + QString url = "httpcachetest_cachecontrol.cgi?max-age=1000"; + QNetworkRequest request(QUrl(TESTFILE + url)); + QNetworkReply *reply = manager.get(request); + QSignalSpy downloaded1(reply, SIGNAL(finished())); + manager.setCache(0); + QTRY_COMPARE(downloaded1.count(), 1); +} + + +QTEST_MAIN(tst_QAbstractNetworkCache) +#include "tst_qabstractnetworkcache.moc" + diff --git a/tests/auto/network/access/qftp/.gitattributes b/tests/auto/network/access/qftp/.gitattributes new file mode 100644 index 0000000000..e04709aa2e --- /dev/null +++ b/tests/auto/network/access/qftp/.gitattributes @@ -0,0 +1 @@ +rfc3252.txt -crlf diff --git a/tests/auto/network/access/qftp/.gitignore b/tests/auto/network/access/qftp/.gitignore new file mode 100644 index 0000000000..7a4845df05 --- /dev/null +++ b/tests/auto/network/access/qftp/.gitignore @@ -0,0 +1,2 @@ +tst_qftp +tst_QFtp_activeMode_inittab diff --git a/tests/auto/network/access/qftp/qftp.pro b/tests/auto/network/access/qftp/qftp.pro new file mode 100644 index 0000000000..8f63d6e26e --- /dev/null +++ b/tests/auto/network/access/qftp/qftp.pro @@ -0,0 +1,22 @@ +load(qttest_p4) +SOURCES += tst_qftp.cpp + + +QT = core network network-private + +wince*: { + addFiles.files = rfc3252.txt + addFiles.path = . + DEPLOYMENT += addFiles + DEFINES += SRCDIR=\\\"\\\" +} else:symbian { + addFiles.files = rfc3252.txt + addFiles.path = . + DEPLOYMENT += addFiles + TARGET.EPOCHEAPSIZE="0x100 0x1000000" + TARGET.CAPABILITY = NetworkServices +} else { + DEFINES += SRCDIR=\\\"$$PWD/\\\" +} + +CONFIG+=insignificant_test # uses live qt-test-server, inherently unstable diff --git a/tests/auto/network/access/qftp/rfc3252.txt b/tests/auto/network/access/qftp/rfc3252.txt new file mode 100644 index 0000000000..b80c61bf0a --- /dev/null +++ b/tests/auto/network/access/qftp/rfc3252.txt @@ -0,0 +1,899 @@ + + + + + + +Network Working Group H. Kennedy +Request for Comments: 3252 Mimezine +Category: Informational 1 April 2002 + + + Binary Lexical Octet Ad-hoc Transport + +Status of this Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2002). All Rights Reserved. + +Abstract + + This document defines a reformulation of IP and two transport layer + protocols (TCP and UDP) as XML applications. + +1. Introduction + +1.1. Overview + + This document describes the Binary Lexical Octet Ad-hoc Transport + (BLOAT): a reformulation of a widely-deployed network-layer protocol + (IP [RFC791]), and two associated transport layer protocols (TCP + [RFC793] and UDP [RFC768]) as XML [XML] applications. It also + describes methods for transporting BLOAT over Ethernet and IEEE 802 + networks as well as encapsulating BLOAT in IP for gatewaying BLOAT + across the public Internet. + +1.2. Motivation + + The wild popularity of XML as a basis for application-level protocols + such as the Blocks Extensible Exchange Protocol [RFC3080], the Simple + Object Access Protocol [SOAP], and Jabber [JABBER] prompted + investigation into the possibility of extending the use of XML in the + protocol stack. Using XML at both the transport and network layer in + addition to the application layer would provide for an amazing amount + of power and flexibility while removing dependencies on proprietary + and hard-to-understand binary protocols. This protocol unification + would also allow applications to use a single XML parser for all + aspects of their operation, eliminating developer time spent figuring + out the intricacies of each new protocol, and moving the hard work of + + + + +Kennedy Informational [Page 1] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + parsing to the XML toolset. The use of XML also mitigates concerns + over "network vs. host" byte ordering which is at the root of many + network application bugs. + +1.3. Relation to Existing Protocols + + The reformulations specified in this RFC follow as closely as + possible the spirit of the RFCs on which they are based, and so MAY + contain elements or attributes that would not be needed in a pure + reworking (e.g. length attributes, which are implicit in XML.) + + The layering of network and transport protocols are maintained in + this RFC despite the optimizations that could be made if the line + were somewhat blurred (i.e. merging TCP and IP into a single, larger + element in the DTD) in order to foster future use of this protocol as + a basis for reformulating other protocols (such as ICMP.) + + Other than the encoding, the behavioral aspects of each of the + existing protocols remain unchanged. Routing, address spaces, TCP + congestion control, etc. behave as specified in the extant standards. + Adapting to new standards and experimental algorithm heuristics for + improving performance will become much easier once the move to BLOAT + has been completed. + +1.4. Requirement Levels + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in BCP 14, RFC 2119 + [RFC2119]. + +2. IPoXML + + This protocol MUST be implemented to be compliant with this RFC. + IPoXML is the root protocol REQUIRED for effective use of TCPoXML + (section 3.) and higher-level application protocols. + + The DTD for this document type can be found in section 7.1. + + The routing of IPoXML can be easily implemented on hosts with an XML + parser, as the regular structure lends itself handily to parsing and + validation of the document/datagram and then processing the + destination address, TTL, and checksum before sending it on to its + next-hop. + + The reformulation of IPv4 was chosen over IPv6 [RFC2460] due to the + wider deployment of IPv4 and the fact that implementing IPv6 as XML + would have exceeded the 1500 byte Ethernet MTU. + + + +Kennedy Informational [Page 2] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + All BLOAT implementations MUST use - and specify - the UTF-8 encoding + of RFC 2279 [RFC2279]. All BLOAT document/datagrams MUST be well- + formed and include the XMLDecl. + +2.1. IP Description + + A number of items have changed (for the better) from the original IP + specification. Bit-masks, where present have been converted into + human-readable values. IP addresses are listed in their dotted- + decimal notation [RFC1123]. Length and checksum values are present + as decimal integers. + + To calculate the length and checksum fields of the IP element, a + canonicalized form of the element MUST be used. The canonical form + SHALL have no whitespace (including newline characters) between + elements and only one space character between attributes. There + SHALL NOT be a space following the last attribute in an element. + + An iterative method SHOULD be used to calculate checksums, as the + length field will vary based on the size of the checksum. + + The payload element bears special attention. Due to the character + set restrictions of XML, the payload of IP datagrams (which MAY + contain arbitrary data) MUST be encoded for transport. This RFC + REQUIRES the contents of the payload to be encoded in the base-64 + encoding of RFC 2045 [RFC2045], but removes the requirement that the + encoded output MUST be wrapped on 76-character lines. + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 3] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +2.2. Example Datagram + + The following is an example IPoXML datagram with an empty payload: + + + + +
+ + + + + + + + + + + + + + + +
+ + +
+ +3. TCPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.2. + +3.1. TCP Description + + A number of items have changed from the original TCP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + To calculate the length and checksum fields of the TCP element, a + canonicalized form of the element MUST be used as in section 2.1. + + An iterative method SHOULD be used to calculate checksums as in + section 2.1. + + The payload element MUST be encoded as in section 2.1. + + + +Kennedy Informational [Page 4] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + The TCP offset element was expanded to a maximum of 255 from 16 to + allow for the increased size of the header in XML. + + TCPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +3.2. Example Datagram + + The following is an example TCPoXML datagram with an empty payload: + + + + + + + + + + + + + + + + + + + + + + + + +4. UDPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.3. + +4.1. UDP Description + + A number of items have changed from the original UDP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + + + + + + +Kennedy Informational [Page 5] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + To calculate the length and checksum fields of the UDP element, a + canonicalized form of the element MUST be used as in section 2.1. An + iterative method SHOULD be used to calculate checksums as in section + 2.1. + + The payload element MUST be encoded as in section 2.1. + + UDPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +4.2. Example Datagram + + The following is an example UDPoXML datagram with an empty payload: + + + + + + + + + + + + + + +5. Network Transport + + This document provides for the transmission of BLOAT datagrams over + two common families of physical layer transport. Future RFCs will + address additional transports as routing vendors catch up to the + specification, and we begin to see BLOAT routed across the Internet + backbone. + +5.1. Ethernet + + BLOAT is encapsulated in Ethernet datagrams as in [RFC894] with the + exception that the type field of the Ethernet frame MUST contain the + value 0xBEEF. The first 5 octets of the Ethernet frame payload will + be 0x3c 3f 78 6d 6c (" + --> + + + + +Kennedy Informational [Page 7] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 9] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 10] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + +7.2. TCPoXML DTD + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 11] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 12] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + +7.3. UDPoXML DTD + + + + + + + + + + + + + + + +Kennedy Informational [Page 13] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +8. Security Considerations + + XML, as a subset of SGML, has the same security considerations as + specified in SGML Media Types [RFC1874]. Security considerations + that apply to IP, TCP and UDP also likely apply to BLOAT as it does + not attempt to correct for issues not related to message format. + +9. References + + [JABBER] Miller, J., "Jabber", draft-miller-jabber-00.txt, + February 2002. (Work in Progress) + + [RFC768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, + August 1980. + + [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791, + September 1981. + + [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC + 793, September 1981. + + [RFC894] Hornig, C., "Standard for the Transmission of IP + Datagrams over Ethernet Networks.", RFC 894, April 1984. + + [RFC1042] Postel, J. and J. Reynolds, "Standard for the + Transmission of IP Datagrams Over IEEE 802 Networks", STD + 43, RFC 1042, February 1988. + + [RFC1123] Braden, R., "Requirements for Internet Hosts - + Application and Support", RFC 1123, October 1989. + + [RFC1874] Levinson, E., "SGML Media Types", RFC 1874, December + 1995. + + [RFC2003] Perkins, C., "IP Encapsulation within IP", RFC 2003, + October 1996. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", RFC 2279, January 1998. + + + + + +Kennedy Informational [Page 14] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6 + (IPv6) Specification", RFC 2460, December 1998. + + [RFC3080] Rose, M., "The Blocks Extensible Exchange Protocol Core", + RFC 3080, March 2001. + + [SOAP] Box, D., Ehnebuske, D., Kakivaya, G., Layman, A., + Mendelsohn, N., Nielsen, H. F., Thatte, S. Winer, D., + "Simple Object Access Protocol (SOAP) 1.1" World Wide Web + Consortium Note, May 2000 http://www.w3.org/TR/SOAP/ + + [XML] Bray, T., Paoli, J., Sperberg-McQueen, C. M., "Extensible + Markup Language (XML)" World Wide Web Consortium + Recommendation REC- xml-19980210. + http://www.w3.org/TR/1998/REC-xml-19980210 + +10. Author's Address + + Hugh Kennedy + Mimezine + 1060 West Addison + Chicago, IL 60613 + USA + + EMail: kennedyh@engin.umich.edu + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 15] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +11. Full Copyright Statement + + Copyright (C) The Internet Society (2002). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 16] + diff --git a/tests/auto/network/access/qftp/tst_qftp.cpp b/tests/auto/network/access/qftp/tst_qftp.cpp new file mode 100644 index 0000000000..117ac92ed7 --- /dev/null +++ b/tests/auto/network/access/qftp/tst_qftp.cpp @@ -0,0 +1,2170 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include + +#include +#include +#include +#include "qftp.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../../network-settings.h" + +//TESTED_CLASS= +//TESTED_FILES= + +#ifdef Q_OS_SYMBIAN +// In Symbian OS test data is located in applications private dir +// Application private dir is default serach path for files, so SRCDIR can be set to empty +#define SRCDIR "" +#endif + +#ifndef QT_NO_BEARERMANAGEMENT +Q_DECLARE_METATYPE(QNetworkConfiguration) +#endif + +class tst_QFtp : public QObject +{ + Q_OBJECT + +public: + tst_QFtp(); + virtual ~tst_QFtp(); + + +public slots: + void initTestCase_data(); + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); +private slots: + void connectToHost_data(); + void connectToHost(); + void connectToUnresponsiveHost(); + void login_data(); + void login(); + void close_data(); + void close(); + + void list_data(); + void list(); + void cd_data(); + void cd(); + void get_data(); + void get(); + void put_data(); + void put(); + void remove(); + void mkdir_data(); + void mkdir(); + void mkdir2(); + void rmdir(); + void rename_data(); + void rename(); + + void commandSequence_data(); + void commandSequence(); + + void abort_data(); + void abort(); + + void bytesAvailable_data(); + void bytesAvailable(); + + void activeMode(); + + void proxy_data(); + void proxy(); + + void binaryAscii(); + + void doneSignal(); + void queueMoreCommandsInDoneSlot(); + + void qtbug7359Crash(); + +protected slots: + void stateChanged( int ); + void listInfo( const QUrlInfo & ); + void readyRead(); + void dataTransferProgress(qint64, qint64); + + void commandStarted( int ); + void commandFinished( int, bool ); + void done( bool ); + void activeModeDone( bool ); + void mkdir2Slot(int id, bool error); + void cdUpSlot(bool); + +private: + QFtp *newFtp(); + void addCommand( QFtp::Command, int ); + bool fileExists( const QString &host, quint16 port, const QString &user, const QString &password, const QString &file, const QString &cdDir = QString::null ); + bool dirExists( const QString &host, quint16 port, const QString &user, const QString &password, const QString &cdDir, const QString &dirToCreate ); + + void renameInit( const QString &host, const QString &user, const QString &password, const QString &createFile ); + void renameCleanup( const QString &host, const QString &user, const QString &password, const QString &fileToDelete ); + + QFtp *ftp; +#ifndef QT_NO_BEARERMANAGEMENT + QSharedPointer networkSessionExplicit; + QSharedPointer networkSessionImplicit; +#endif + + QList ids; // helper to make sure that all expected signals are emitted + int current_id; + + int connectToHost_state; + int close_state; + int login_state; + int cur_state; + struct CommandResult + { + int id; + int success; + }; + QMap< QFtp::Command, CommandResult > resultMap; + typedef QMap::Iterator ResMapIt; + + int done_success; + int commandSequence_success; + + qlonglong bytesAvailable_finishedGet; + qlonglong bytesAvailable_finished; + qlonglong bytesAvailable_done; + + QList listInfo_i; + QByteArray newData_ba; + qlonglong bytesTotal; + qlonglong bytesDone; + + bool inFileDirExistsFunction; + + QString uniqueExtension; +}; + +//#define DUMP_SIGNALS + +const int bytesTotal_init = -10; +const int bytesDone_init = -10; + +tst_QFtp::tst_QFtp() : + ftp(0) +{ +} + +tst_QFtp::~tst_QFtp() +{ +} + +void tst_QFtp::initTestCase_data() +{ + QTest::addColumn("setProxy"); + QTest::addColumn("proxyType"); + QTest::addColumn("setSession"); + + QTest::newRow("WithoutProxy") << false << 0 << false; + QTest::newRow("WithSocks5Proxy") << true << int(QNetworkProxy::Socks5Proxy) << false; + //### doesn't work well yet. + //QTest::newRow("WithHttpProxy") << true << int(QNetworkProxy::HttpProxy); + +#ifndef QT_NO_BEARERMANAGEMENT + QTest::newRow("WithoutProxyWithSession") << false << 0 << true; + QTest::newRow("WithSocks5ProxyAndSession") << true << int(QNetworkProxy::Socks5Proxy) << true; +#endif +} + +void tst_QFtp::initTestCase() +{ +#ifndef QT_NO_BEARERMANAGEMENT + QNetworkConfigurationManager manager; + networkSessionImplicit = QSharedPointer(new QNetworkSession(manager.defaultConfiguration())); + networkSessionImplicit->open(); + QVERIFY(networkSessionImplicit->waitForOpened(60000)); //there may be user prompt on 1st connect +#endif +} + +void tst_QFtp::cleanupTestCase() +{ +#ifndef QT_NO_BEARERMANAGEMENT + networkSessionExplicit.clear(); + networkSessionImplicit.clear(); +#endif +} + +void tst_QFtp::init() +{ + QFETCH_GLOBAL(bool, setProxy); + QFETCH_GLOBAL(int, proxyType); + QFETCH_GLOBAL(bool, setSession); + if (setProxy) { + if (proxyType == QNetworkProxy::Socks5Proxy) { + QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080)); + } else if (proxyType == QNetworkProxy::HttpProxy) { + QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3128)); + } + } +#ifndef QT_NO_BEARERMANAGEMENT + if (setSession) { + networkSessionExplicit = networkSessionImplicit; + if (!networkSessionExplicit->isOpen()) { + networkSessionExplicit->open(); + QVERIFY(networkSessionExplicit->waitForOpened(30000)); + } + } else { + networkSessionExplicit.clear(); + } +#endif + + delete ftp; + ftp = 0; + + ids.clear(); + current_id = 0; + + resultMap.clear(); + connectToHost_state = -1; + close_state = -1; + login_state = -1; + cur_state = QFtp::Unconnected; + + listInfo_i.clear(); + newData_ba = QByteArray(); + bytesTotal = bytesTotal_init; + bytesDone = bytesDone_init; + + done_success = -1; + commandSequence_success = -1; + + bytesAvailable_finishedGet = 1234567890; + bytesAvailable_finished = 1234567890; + bytesAvailable_done = 1234567890; + + inFileDirExistsFunction = FALSE; + +#if !defined(Q_OS_WINCE) + srand(time(0)); + uniqueExtension = QString("%1%2%3").arg((qulonglong)this).arg(rand()).arg((qulonglong)time(0)); +#else + srand(0); + uniqueExtension = QString("%1%2%3").arg((qulonglong)this).arg(rand()).arg((qulonglong)(0)); +#endif +} + +void tst_QFtp::cleanup() +{ + if (ftp) { + delete ftp; + ftp = 0; + } + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) { + QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy); + } + + delete ftp; + ftp = 0; +#ifndef QT_NO_BEARERMANAGEMENT + networkSessionExplicit.clear(); +#endif +} + +void tst_QFtp::connectToHost_data() +{ + QTest::addColumn("host"); + QTest::addColumn("port"); + QTest::addColumn("state"); + + QTest::newRow( "ok01" ) << QtNetworkSettings::serverName() << (uint)21 << (int)QFtp::Connected; + QTest::newRow( "error01" ) << QtNetworkSettings::serverName() << (uint)2222 << (int)QFtp::Unconnected; + QTest::newRow( "error02" ) << QString("foo.bar") << (uint)21 << (int)QFtp::Unconnected; +} + +void tst_QFtp::connectToHost() +{ + QFETCH( QString, host ); + QFETCH( uint, port ); + + ftp = newFtp(); + addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) ); + + QTestEventLoop::instance().enterLoop( 61 ); + delete ftp; + ftp = 0; + if ( QTestEventLoop::instance().timeout() ) + QFAIL( "Network operation timed out" ); + + QTEST( connectToHost_state, "state" ); + + ResMapIt it = resultMap.find( QFtp::ConnectToHost ); + QVERIFY( it != resultMap.end() ); + QFETCH( int, state ); + if ( state == QFtp::Connected ) { + QVERIFY( it.value().success == 1 ); + } else { + QVERIFY( it.value().success == 0 ); + } +} + +void tst_QFtp::connectToUnresponsiveHost() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + QSKIP( "This test takes too long if we test with proxies too", SkipSingle ); + + QString host = "192.0.2.42"; // IP out of TEST-NET, should be unreachable + uint port = 21; + + ftp = newFtp(); + addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) ); + + qDebug( "About to connect to host that won't reply (this test takes 60 seconds)" ); + QTestEventLoop::instance().enterLoop( 61 ); +#ifdef Q_OS_WIN + /* On Windows, we do not get a timeout, because Winsock is behaving in a strange way: + We issue two "WSAConnect()" calls, after the first, as a result we get WSAEWOULDBLOCK, + after the second, we get WSAEISCONN, which means that the socket is connected, which cannot be. + However, after some seconds we get a socket error saying that the remote host closed the connection, + which can neither be. For this test, that would actually enable us to finish before timout, but handling that case + (in void QFtpPI::error(QAbstractSocket::SocketError e)) breaks + a lot of other stuff in QFtp, so we just expect this test to fail on Windows. + */ + QEXPECT_FAIL("", "timeout not working due to strange Windows socket behaviour (see source file of this test for explanation)", Abort); +#else + QEXPECT_FAIL("", "QTBUG-20687", Abort); +#endif + QVERIFY2(! QTestEventLoop::instance().timeout(), "Network timeout longer than expected (should have been 60 seconds)"); + + QVERIFY( ftp->state() == QFtp::Unconnected); + ResMapIt it = resultMap.find( QFtp::ConnectToHost ); + QVERIFY( it != resultMap.end() ); + QVERIFY( it.value().success == 0 ); + + delete ftp; + ftp = 0; +} + +void tst_QFtp::login_data() +{ + QTest::addColumn("host"); + QTest::addColumn("port"); + QTest::addColumn("user"); + QTest::addColumn("password"); + QTest::addColumn("success"); + + QTest::newRow( "ok01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << 1; + QTest::newRow( "ok02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftp") << QString() << 1; + QTest::newRow( "ok03" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftp") << QString("foo") << 1; + QTest::newRow( "ok04" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") << 1; + + QTest::newRow( "error01" ) << QtNetworkSettings::serverName() << (uint)21 << QString("foo") << QString() << 0; + QTest::newRow( "error02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("foo") << QString("bar") << 0; +} + +void tst_QFtp::login() +{ + QFETCH( QString, host ); + QFETCH( uint, port ); + QFETCH( QString, user ); + QFETCH( QString, password ); + + ftp = newFtp(); + addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) ); + addCommand( QFtp::Login, ftp->login( user, password ) ); + + QTestEventLoop::instance().enterLoop( 30 ); + delete ftp; + ftp = 0; + if ( QTestEventLoop::instance().timeout() ) + QFAIL( "Network operation timed out" ); + + ResMapIt it = resultMap.find( QFtp::Login ); + QVERIFY( it != resultMap.end() ); + QTEST( it.value().success, "success" ); + + if ( it.value().success ) { + QVERIFY( login_state == QFtp::LoggedIn ); + } else { + QVERIFY( login_state != QFtp::LoggedIn ); + } +} + +void tst_QFtp::close_data() +{ + QTest::addColumn("host"); + QTest::addColumn("port"); + QTest::addColumn("user"); + QTest::addColumn("password"); + QTest::addColumn("login"); + + QTest::newRow( "login01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << (bool)TRUE; + QTest::newRow( "login02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftp") << QString() << (bool)TRUE; + QTest::newRow( "login03" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftp") << QString("foo") << (bool)TRUE; + QTest::newRow( "login04" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") << (bool)TRUE; + + QTest::newRow( "no-login01" ) << QtNetworkSettings::serverName() << (uint)21 << QString("") << QString("") << (bool)FALSE; +} + +void tst_QFtp::close() +{ + QFETCH( QString, host ); + QFETCH( uint, port ); + QFETCH( QString, user ); + QFETCH( QString, password ); + QFETCH( bool, login ); + + ftp = newFtp(); + addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) ); + if ( login ) + addCommand( QFtp::Login, ftp->login( user, password ) ); + addCommand( QFtp::Close, ftp->close() ); + + QTestEventLoop::instance().enterLoop( 30 ); + delete ftp; + ftp = 0; + if ( QTestEventLoop::instance().timeout() ) + QFAIL( "Network operation timed out" ); + + QCOMPARE( close_state, (int)QFtp::Unconnected ); + + ResMapIt it = resultMap.find( QFtp::Close ); + QVERIFY( it != resultMap.end() ); + QVERIFY( it.value().success == 1 ); +} + +void tst_QFtp::list_data() +{ + QTest::addColumn("host"); + QTest::addColumn("port"); + QTest::addColumn("user"); + QTest::addColumn("password"); + QTest::addColumn("dir"); + QTest::addColumn("success"); + QTest::addColumn("entryNames"); // ### we should rather use a QList here + + QStringList flukeRoot; + flukeRoot << "pub"; + flukeRoot << "qtest"; + QStringList flukeQtest; + flukeQtest << "bigfile"; + flukeQtest << "nonASCII"; + flukeQtest << "rfc3252"; + flukeQtest << "rfc3252.txt"; + flukeQtest << "upload"; + + QTest::newRow( "workDir01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString() << 1 << flukeRoot; + QTest::newRow( "workDir02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") << QString() << 1 << flukeRoot; + + QTest::newRow( "relPath01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("qtest") << 1 << flukeQtest; + QTest::newRow( "relPath02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") << QString("qtest") << 1 << flukeQtest; + + QTest::newRow( "absPath01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("/qtest") << 1 << flukeQtest; + QTest::newRow( "absPath02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") << QString("/var/ftp/qtest") << 1 << flukeQtest; + + QTest::newRow( "nonExist01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("foo") << 1 << QStringList(); + QTest::newRow( "nonExist02" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("/foo") << 1 << QStringList(); + // ### The microsoft server does not seem to work properly at the moment -- + // I am also not able to open a data connection with other, non-Qt FTP + // clients to it. + // QTest::newRow( "nonExist03" ) << "ftp.microsoft.com" << (uint)21 << QString() << QString() << QString("/foo") << 0 << QStringList(); + + QStringList susePub; + susePub << "README.mirror-policy" << "axp" << "i386" << "ia64" << "install" << "noarch" << "pubring.gpg-build.suse.de" << "update" << "x86_64"; + QTest::newRow( "epsvNotSupported" ) << QString("ftp.funet.fi") << (uint)21 << QString::fromLatin1("ftp") << QString::fromLatin1("root@") << QString("/pub/Linux/suse/suse") << 1 << susePub; +} + +void tst_QFtp::list() +{ + QFETCH( QString, host ); + QFETCH( uint, port ); + QFETCH( QString, user ); + QFETCH( QString, password ); + QFETCH( QString, dir ); + + ftp = newFtp(); + addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) ); + addCommand( QFtp::Login, ftp->login( user, password ) ); + addCommand( QFtp::List, ftp->list( dir ) ); + addCommand( QFtp::Close, ftp->close() ); + + QTestEventLoop::instance().enterLoop( 30 ); + delete ftp; + ftp = 0; + if ( QTestEventLoop::instance().timeout() ) + QFAIL( "Network operation timed out" ); + + ResMapIt it = resultMap.find( QFtp::List ); + QVERIFY( it != resultMap.end() ); + QTEST( it.value().success, "success" ); + QFETCH( QStringList, entryNames ); + QCOMPARE( listInfo_i.count(), entryNames.count() ); + for ( uint i=0; i < (uint) entryNames.count(); i++ ) { + QCOMPARE( listInfo_i[i].name(), entryNames[i] ); + } +} + +void tst_QFtp::cd_data() +{ + QTest::addColumn("host"); + QTest::addColumn("port"); + QTest::addColumn("user"); + QTest::addColumn("password"); + QTest::addColumn("dir"); + QTest::addColumn("success"); + QTest::addColumn("entryNames"); // ### we should rather use a QList here + + QStringList flukeRoot; + flukeRoot << "qtest"; + QStringList flukeQtest; + flukeQtest << "bigfile"; + flukeQtest << "nonASCII"; + flukeQtest << "rfc3252"; + flukeQtest << "rfc3252.txt"; + flukeQtest << "upload"; + + QTest::newRow( "relPath01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("qtest") << 1 << flukeQtest; + QTest::newRow( "relPath02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") << QString("qtest") << 1 << flukeQtest; + + QTest::newRow( "absPath01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("/qtest") << 1 << flukeQtest; + QTest::newRow( "absPath02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") << QString("/var/ftp/qtest") << 1 << flukeQtest; + + QTest::newRow( "nonExist01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("foo") << 0 << QStringList(); + QTest::newRow( "nonExist03" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("/foo") << 0 << QStringList(); +} + +void tst_QFtp::cd() +{ + QFETCH( QString, host ); + QFETCH( uint, port ); + QFETCH( QString, user ); + QFETCH( QString, password ); + QFETCH( QString, dir ); + + ftp = newFtp(); + addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) ); + addCommand( QFtp::Login, ftp->login( user, password ) ); + addCommand( QFtp::Cd, ftp->cd( dir ) ); + addCommand( QFtp::List, ftp->list() ); + addCommand( QFtp::Close, ftp->close() ); + + QTestEventLoop::instance().enterLoop( 30 ); + + delete ftp; + ftp = 0; + if ( QTestEventLoop::instance().timeout() ) { + QFAIL( "Network operation timed out" ); + } + + ResMapIt it = resultMap.find( QFtp::Cd ); + QVERIFY( it != resultMap.end() ); + QTEST( it.value().success, "success" ); + QFETCH( QStringList, entryNames ); + QCOMPARE( listInfo_i.count(), entryNames.count() ); + for ( uint i=0; i < (uint) entryNames.count(); i++ ) { + QCOMPARE( listInfo_i[i].name(), entryNames[i] ); + } +} + +void tst_QFtp::get_data() +{ + QTest::addColumn("host"); + QTest::addColumn("port"); + QTest::addColumn("user"); + QTest::addColumn("password"); + QTest::addColumn("file"); + QTest::addColumn("success"); + QTest::addColumn("res"); + QTest::addColumn("useIODevice"); + + // ### move this into external testdata + QFile file( SRCDIR "rfc3252.txt" ); + QVERIFY( file.open( QIODevice::ReadOnly ) ); + QByteArray rfc3252 = file.readAll(); + + // test the two get() overloads in one routine + for ( int i=0; i<2; i++ ) { + QTest::newRow( QString("relPath01_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() + << "qtest/rfc3252" << 1 << rfc3252 << (bool)(i==1); + QTest::newRow( QString("relPath02_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") + << "qtest/rfc3252" << 1 << rfc3252 << (bool)(i==1); + + QTest::newRow( QString("absPath01_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() + << "/qtest/rfc3252" << 1 << rfc3252 << (bool)(i==1); + QTest::newRow( QString("absPath02_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") + << "/var/ftp/qtest/rfc3252" << 1 << rfc3252 << (bool)(i==1); + + QTest::newRow( QString("nonExist01_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() + << QString("foo") << 0 << QByteArray() << (bool)(i==1); + QTest::newRow( QString("nonExist02_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() + << QString("/foo") << 0 << QByteArray() << (bool)(i==1); + } +} + +void tst_QFtp::get() +{ + // for the overload that takes a QIODevice + QByteArray buf_ba; + QBuffer buf( &buf_ba ); + + QFETCH( QString, host ); + QFETCH( uint, port ); + QFETCH( QString, user ); + QFETCH( QString, password ); + QFETCH( QString, file ); + QFETCH( bool, useIODevice ); + + ftp = newFtp(); + addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) ); + addCommand( QFtp::Login, ftp->login( user, password ) ); + if ( useIODevice ) { + buf.open( QIODevice::WriteOnly ); + addCommand( QFtp::Get, ftp->get( file, &buf ) ); + } else { + addCommand( QFtp::Get, ftp->get( file ) ); + } + addCommand( QFtp::Close, ftp->close() ); + + QTestEventLoop::instance().enterLoop( 50 ); + delete ftp; + ftp = 0; + if ( QTestEventLoop::instance().timeout() ) + QFAIL( "Network operation timed out" ); + + ResMapIt it = resultMap.find( QFtp::Get ); + QVERIFY( it != resultMap.end() ); + QTEST( it.value().success, "success" ); + if ( useIODevice ) { + QTEST( buf_ba, "res" ); + } else { + QTEST( newData_ba, "res" ); + } + QVERIFY( bytesTotal != bytesTotal_init ); + if ( bytesTotal != -1 ) { + QVERIFY( bytesDone == bytesTotal ); + } + if ( useIODevice ) { + if ( bytesDone != bytesDone_init ) { + QVERIFY( (int)buf_ba.size() == bytesDone ); + } + } else { + if ( bytesDone != bytesDone_init ) { + QVERIFY( (int)newData_ba.size() == bytesDone ); + } + } +} + +void tst_QFtp::put_data() +{ + QTest::addColumn("host"); + QTest::addColumn("port"); + QTest::addColumn("user"); + QTest::addColumn("password"); + QTest::addColumn("file"); + QTest::addColumn("fileData"); + QTest::addColumn("useIODevice"); + QTest::addColumn("success"); + + // ### move this into external testdata + QFile file( SRCDIR "rfc3252.txt" ); + QVERIFY( file.open( QIODevice::ReadOnly ) ); + QByteArray rfc3252 = file.readAll(); + + QByteArray bigData( 10*1024*1024, 0 ); + bigData.fill( 'A' ); + + // test the two put() overloads in one routine + for ( int i=0; i<2; i++ ) { + QTest::newRow( QString("relPath01_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() + << QString("qtest/upload/rel01_%1") << rfc3252 + << (bool)(i==1) << 1; + /* + QTest::newRow( QString("relPath02_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") + << QString("qtest/upload/rel02_%1") << rfc3252 + << (bool)(i==1) << 1; + QTest::newRow( QString("relPath03_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") + << QString("qtest/upload/rel03_%1") << QByteArray() + << (bool)(i==1) << 1; + QTest::newRow( QString("relPath04_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") + << QString("qtest/upload/rel04_%1") << bigData + << (bool)(i==1) << 1; + + QTest::newRow( QString("absPath01_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() + << QString("/qtest/upload/abs01_%1") << rfc3252 + << (bool)(i==1) << 1; + QTest::newRow( QString("absPath02_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") + << QString("/srv/ftp/qtest/upload/abs02_%1") << rfc3252 + << (bool)(i==1) << 1; + + QTest::newRow( QString("nonExist01_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() + << QString("foo") << QByteArray() + << (bool)(i==1) << 0; + QTest::newRow( QString("nonExist02_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() + << QString("/foo") << QByteArray() + << (bool)(i==1) << 0; +*/ + } +} + +void tst_QFtp::put() +{ + QFETCH( QString, host ); + QFETCH( uint, port ); + QFETCH( QString, user ); + QFETCH( QString, password ); + QFETCH( QString, file ); + QFETCH( QByteArray, fileData ); + QFETCH( bool, useIODevice ); + +#ifdef Q_OS_WIN + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) { + QFETCH_GLOBAL(int, proxyType); + if (proxyType == QNetworkProxy::Socks5Proxy) { + QSKIP("With socks5 the put() test takes too long time on Windows.", SkipAll); + } + } +#endif + + const int timestep = 50; + + if(file.contains('%')) + file = file.arg(uniqueExtension); + + // for the overload that takes a QIODevice + QBuffer buf_fileData( &fileData ); + buf_fileData.open( QIODevice::ReadOnly ); + + ResMapIt it; + ////////////////////////////////////////////////////////////////// + // upload the file + init(); + ftp = newFtp(); + addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) ); + addCommand( QFtp::Login, ftp->login( user, password ) ); + if ( useIODevice ) + addCommand( QFtp::Put, ftp->put( &buf_fileData, file ) ); + else + addCommand( QFtp::Put, ftp->put( fileData, file ) ); + addCommand( QFtp::Close, ftp->close() ); + + for(int time = 0; time <= fileData.length() / 20000; time += timestep) { + QTestEventLoop::instance().enterLoop( timestep ); + if(ftp->currentCommand() == QFtp::None) + break; + } + delete ftp; + ftp = 0; + if ( QTestEventLoop::instance().timeout() ) + QFAIL( "Network operation timed out" ); + + it = resultMap.find( QFtp::Put ); + QVERIFY( it != resultMap.end() ); + QTEST( it.value().success, "success" ); + if ( !it.value().success ) { + QVERIFY( !fileExists( host, port, user, password, file ) ); + return; // the following tests are only meaningful if the file could be put + } + QVERIFY( bytesTotal == (int)fileData.size() ); + QVERIFY( bytesDone == bytesTotal ); + + QVERIFY( fileExists( host, port, user, password, file ) ); + + ////////////////////////////////////////////////////////////////// + // fetch file to make sure that it is equal to the uploaded file + init(); + ftp = newFtp(); + QBuffer buf; + buf.open( QIODevice::WriteOnly ); + addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) ); + addCommand( QFtp::Login, ftp->login( user, password ) ); + addCommand( QFtp::Get, ftp->get( file, &buf ) ); + addCommand( QFtp::Close, ftp->close() ); + + for(int time = 0; time <= fileData.length() / 20000; time += timestep) { + QTestEventLoop::instance().enterLoop( timestep ); + if(ftp->currentCommand() == QFtp::None) + break; + } + delete ftp; + ftp = 0; + if ( QTestEventLoop::instance().timeout() ) + QFAIL( "Network operation timed out" ); + + QVERIFY( done_success == 1 ); + QTEST( buf.buffer(), "fileData" ); + + ////////////////////////////////////////////////////////////////// + // cleanup (i.e. remove the file) -- this also tests the remove command + init(); + ftp = newFtp(); + addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) ); + addCommand( QFtp::Login, ftp->login( user, password ) ); + addCommand( QFtp::Remove, ftp->remove( file ) ); + addCommand( QFtp::Close, ftp->close() ); + + QTestEventLoop::instance().enterLoop( timestep ); + delete ftp; + ftp = 0; + if ( QTestEventLoop::instance().timeout() ) + QFAIL( "Network operation timed out" ); + + it = resultMap.find( QFtp::Remove ); + QVERIFY( it != resultMap.end() ); + QCOMPARE( it.value().success, 1 ); + + QVERIFY( !fileExists( host, port, user, password, file ) ); +} + +void tst_QFtp::remove() +{ + DEPENDS_ON( "put" ); +} + +void tst_QFtp::mkdir_data() +{ + QTest::addColumn("host"); + QTest::addColumn("port"); + QTest::addColumn("user"); + QTest::addColumn("password"); + QTest::addColumn("cdDir"); + QTest::addColumn("dirToCreate"); + QTest::addColumn("success"); + + QTest::newRow( "relPath01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() + << "qtest/upload" << QString("rel01_%1") << 1; + QTest::newRow( "relPath02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") + << "qtest/upload" << QString("rel02_%1") << 1; + QTest::newRow( "relPath03" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") + << "qtest/upload" << QString("rel03_%1") << 1; + + QTest::newRow( "absPath01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() + << "." << QString("/qtest/upload/abs01_%1") << 1; + QTest::newRow( "absPath02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") + << "." << QString("/var/ftp/qtest/upload/abs02_%1") << 1; + + // QTest::newRow( "nonExist01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("foo") << 0; + QTest::newRow( "nonExist01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() + << "." << QString("foo") << 0; + QTest::newRow( "nonExist02" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() + << "." << QString("/foo") << 0; +} + +void tst_QFtp::mkdir() +{ + QFETCH( QString, host ); + QFETCH( uint, port ); + QFETCH( QString, user ); + QFETCH( QString, password ); + QFETCH( QString, cdDir ); + QFETCH( QString, dirToCreate ); + + if(dirToCreate.contains('%')) + dirToCreate = dirToCreate.arg(uniqueExtension); + + ////////////////////////////////////////////////////////////////// + // create the directory + init(); + ftp = newFtp(); + addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) ); + addCommand( QFtp::Login, ftp->login( user, password ) ); + addCommand( QFtp::Cd, ftp->cd( cdDir ) ); + addCommand( QFtp::Mkdir, ftp->mkdir( dirToCreate ) ); + addCommand( QFtp::Close, ftp->close() ); + + QTestEventLoop::instance().enterLoop( 30 ); + delete ftp; + ftp = 0; + if ( QTestEventLoop::instance().timeout() ) + QFAIL( "Network operation timed out" ); + + ResMapIt it = resultMap.find( QFtp::Mkdir ); + QVERIFY( it != resultMap.end() ); + QTEST( it.value().success, "success" ); + if ( !it.value().success ) { + QVERIFY( !dirExists( host, port, user, password, cdDir, dirToCreate ) ); + return; // the following tests are only meaningful if the dir could be created + } + QVERIFY( dirExists( host, port, user, password, cdDir, dirToCreate ) ); + + ////////////////////////////////////////////////////////////////// + // create the directory again (should always fail!) + init(); + ftp = newFtp(); + addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) ); + addCommand( QFtp::Login, ftp->login( user, password ) ); + addCommand( QFtp::Cd, ftp->cd( cdDir ) ); + addCommand( QFtp::Mkdir, ftp->mkdir( dirToCreate ) ); + addCommand( QFtp::Close, ftp->close() ); + + QTestEventLoop::instance().enterLoop( 30 ); + delete ftp; + ftp = 0; + if ( QTestEventLoop::instance().timeout() ) + QFAIL( "Network operation timed out" ); + + it = resultMap.find( QFtp::Mkdir ); + QVERIFY( it != resultMap.end() ); + QCOMPARE( it.value().success, 0 ); + + ////////////////////////////////////////////////////////////////// + // remove the directory + init(); + ftp = newFtp(); + addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) ); + addCommand( QFtp::Login, ftp->login( user, password ) ); + addCommand( QFtp::Cd, ftp->cd( cdDir ) ); + addCommand( QFtp::Rmdir, ftp->rmdir( dirToCreate ) ); + addCommand( QFtp::Close, ftp->close() ); + + QTestEventLoop::instance().enterLoop( 30 ); + delete ftp; + ftp = 0; + if ( QTestEventLoop::instance().timeout() ) + QFAIL( "Network operation timed out" ); + + it = resultMap.find( QFtp::Rmdir ); + QVERIFY( it != resultMap.end() ); + QCOMPARE( it.value().success, 1 ); + + QVERIFY( !dirExists( host, port, user, password, cdDir, dirToCreate ) ); +} + +void tst_QFtp::mkdir2() +{ + ftp = new QFtp; + ftp->connectToHost(QtNetworkSettings::serverName()); + ftp->login(); + current_id = ftp->cd("kake/test"); + + QEventLoop loop; + connect(ftp, SIGNAL(done(bool)), &loop, SLOT(quit())); + connect(ftp, SIGNAL(commandFinished(int, bool)), this, SLOT(mkdir2Slot(int, bool))); + QTimer::singleShot(5000, &loop, SLOT(quit())); + + QSignalSpy commandStartedSpy(ftp, SIGNAL(commandStarted(int))); + QSignalSpy commandFinishedSpy(ftp, SIGNAL(commandFinished(int, bool))); + + loop.exec(); + + QCOMPARE(commandStartedSpy.count(), 4); // connect, login, cd, mkdir + QCOMPARE(commandFinishedSpy.count(), 4); + + for (int i = 0; i < 4; ++i) + QCOMPARE(commandFinishedSpy.at(i).at(0), commandStartedSpy.at(i).at(0)); + + QVERIFY(!commandFinishedSpy.at(0).at(1).toBool()); + QVERIFY(!commandFinishedSpy.at(1).at(1).toBool()); + QVERIFY(commandFinishedSpy.at(2).at(1).toBool()); + QVERIFY(commandFinishedSpy.at(3).at(1).toBool()); + + delete ftp; + ftp = 0; +} + +void tst_QFtp::mkdir2Slot(int id, bool) +{ + if (id == current_id) + ftp->mkdir("kake/test"); +} + +void tst_QFtp::rmdir() +{ + DEPENDS_ON( "mkdir" ); +} + +void tst_QFtp::rename_data() +{ + QTest::addColumn("host"); + QTest::addColumn("user"); + QTest::addColumn("password"); + QTest::addColumn("cdDir"); + QTest::addColumn("oldfile"); + QTest::addColumn("newfile"); + QTest::addColumn("createFile"); + QTest::addColumn("renamedFile"); + QTest::addColumn("success"); + + QTest::newRow("relPath01") << QtNetworkSettings::serverName() << QString() << QString() + << "qtest/upload" + << QString("rel_old01_%1") << QString("rel_new01_%1") + << QString("qtest/upload/rel_old01_%1") << QString("qtest/upload/rel_new01_%1") + << 1; + QTest::newRow("relPath02") << QtNetworkSettings::serverName() << QString("ftptest") << "password" + << "qtest/upload" + << QString("rel_old02_%1") << QString("rel_new02_%1") + << QString("qtest/upload/rel_old02_%1") << QString("qtest/upload/rel_new02_%1") + << 1; + QTest::newRow("relPath03") << QtNetworkSettings::serverName() << QString("ftptest") << "password" + << "qtest/upload" + << QString("rel_old03_%1")<< QString("rel_new03_%1") + << QString("qtest/upload/rel_old03_%1") << QString("qtest/upload/rel_new03_%1") + << 1; + + QTest::newRow("absPath01") << QtNetworkSettings::serverName() << QString() << QString() + << QString() + << QString("/qtest/upload/abs_old01_%1") << QString("/qtest/upload/abs_new01_%1") + << QString("/qtest/upload/abs_old01_%1") << QString("/qtest/upload/abs_new01_%1") + << 1; + QTest::newRow("absPath02") << QtNetworkSettings::serverName() << QString("ftptest") << "password" + << QString() + << QString("/var/ftp/qtest/upload/abs_old02_%1") << QString("/var/ftp/qtest/upload/abs_new02_%1") + << QString("/var/ftp/qtest/upload/abs_old02_%1") << QString("/var/ftp/qtest/upload/abs_new02_%1") + << 1; + + QTest::newRow("nonExist01") << QtNetworkSettings::serverName() << QString() << QString() + << QString() + << QString("foo") << "new_foo" + << QString() << QString() + << 0; + QTest::newRow("nonExist02") << QtNetworkSettings::serverName() << QString() << QString() + << QString() + << QString("/foo") << QString("/new_foo") + << QString() << QString() + << 0; +} + +void tst_QFtp::renameInit( const QString &host, const QString &user, const QString &password, const QString &createFile ) +{ + if ( !createFile.isNull() ) { + // upload the file + init(); + ftp = newFtp(); + addCommand( QFtp::ConnectToHost, ftp->connectToHost( host ) ); + addCommand( QFtp::Login, ftp->login( user, password ) ); + addCommand( QFtp::Put, ftp->put( QByteArray(), createFile ) ); + addCommand( QFtp::Close, ftp->close() ); + + QTestEventLoop::instance().enterLoop( 50 ); + delete ftp; + ftp = 0; + if ( QTestEventLoop::instance().timeout() ) + QFAIL( "Network operation timed out" ); + + ResMapIt it = resultMap.find( QFtp::Put ); + QVERIFY( it != resultMap.end() ); + QVERIFY( it.value().success == 1 ); + + QVERIFY( fileExists( host, 21, user, password, createFile ) ); + } +} + +void tst_QFtp::renameCleanup( const QString &host, const QString &user, const QString &password, const QString &fileToDelete ) +{ + if ( !fileToDelete.isNull() ) { + // cleanup (i.e. remove the file) + init(); + ftp = newFtp(); + addCommand( QFtp::ConnectToHost, ftp->connectToHost( host ) ); + addCommand( QFtp::Login, ftp->login( user, password ) ); + addCommand( QFtp::Remove, ftp->remove( fileToDelete ) ); + addCommand( QFtp::Close, ftp->close() ); + + QTestEventLoop::instance().enterLoop( 30 ); + delete ftp; + ftp = 0; + if ( QTestEventLoop::instance().timeout() ) + QFAIL( "Network operation timed out" ); + + ResMapIt it = resultMap.find( QFtp::Remove ); + QVERIFY( it != resultMap.end() ); + QVERIFY( it.value().success == 1 ); + + QVERIFY( !fileExists( host, 21, user, password, fileToDelete ) ); + } +} + +void tst_QFtp::rename() +{ + QFETCH( QString, host ); + QFETCH( QString, user ); + QFETCH( QString, password ); + QFETCH( QString, cdDir ); + QFETCH( QString, oldfile ); + QFETCH( QString, newfile ); + QFETCH( QString, createFile ); + QFETCH( QString, renamedFile ); + + if(oldfile.contains('%')) + oldfile = oldfile.arg(uniqueExtension); + if(newfile.contains('%')) + newfile = newfile.arg(uniqueExtension); + if(createFile.contains('%')) + createFile = createFile.arg(uniqueExtension); + if(renamedFile.contains('%')) + renamedFile = renamedFile.arg(uniqueExtension); + + renameInit( host, user, password, createFile ); + + init(); + ftp = newFtp(); + addCommand( QFtp::ConnectToHost, ftp->connectToHost( host ) ); + addCommand( QFtp::Login, ftp->login( user, password ) ); + if ( !cdDir.isNull() ) + addCommand( QFtp::Cd, ftp->cd( cdDir ) ); + addCommand( QFtp::Rename, ftp->rename( oldfile, newfile ) ); + addCommand( QFtp::Close, ftp->close() ); + + QTestEventLoop::instance().enterLoop( 30 ); + delete ftp; + ftp = 0; + if ( QTestEventLoop::instance().timeout() ) + QFAIL( "Network operation timed out" ); + + ResMapIt it = resultMap.find( QFtp::Rename ); + QVERIFY( it != resultMap.end() ); + QTEST( it.value().success, "success" ); + + if ( it.value().success ) { + QVERIFY( !fileExists( host, 21, user, password, oldfile, cdDir ) ); + QVERIFY( fileExists( host, 21, user, password, newfile, cdDir ) ); + QVERIFY( fileExists( host, 21, user, password, renamedFile ) ); + } else { + QVERIFY( !fileExists( host, 21, user, password, newfile, cdDir ) ); + QVERIFY( !fileExists( host, 21, user, password, renamedFile ) ); + } + + renameCleanup( host, user, password, renamedFile ); +} + +/* + The commandSequence() test does not test any particular function. It rather + tests a sequence of arbitrary commands specified in the test data. +*/ +class FtpCommand +{ +public: + FtpCommand() : + cmd(QFtp::None) + { } + + FtpCommand( QFtp::Command command ) : + cmd(command) + { } + + FtpCommand( QFtp::Command command, const QStringList &arguments ) : + cmd(command), args(arguments) + { } + + FtpCommand( const FtpCommand &c ) + { *this = c; } + + FtpCommand &operator=( const FtpCommand &c ) + { + this->cmd = c.cmd; + this->args = c.args; + return *this; + } + + QFtp::Command cmd; + QStringList args; +}; +QDataStream &operator<<( QDataStream &s, const FtpCommand &command ) +{ + s << (int)command.cmd; + s << command.args; + return s; +} +QDataStream &operator>>( QDataStream &s, FtpCommand &command ) +{ + int tmp; + s >> tmp; + command.cmd = (QFtp::Command)tmp; + s >> command.args; + return s; +} +Q_DECLARE_METATYPE(QList) + +void tst_QFtp::commandSequence_data() +{ + // some "constants" + QStringList argConnectToHost01; + argConnectToHost01 << QtNetworkSettings::serverName() << "21"; + + QStringList argLogin01, argLogin02, argLogin03, argLogin04; + argLogin01 << QString() << QString(); + argLogin02 << "ftp" << QString(); + argLogin03 << "ftp" << "foo"; + argLogin04 << QString("ftptest") << "password"; + + FtpCommand connectToHost01( QFtp::ConnectToHost, argConnectToHost01 ); + FtpCommand login01( QFtp::Login, argLogin01 ); + FtpCommand login02( QFtp::Login, argLogin01 ); + FtpCommand login03( QFtp::Login, argLogin01 ); + FtpCommand login04( QFtp::Login, argLogin01 ); + FtpCommand close01( QFtp::Close ); + + QTest::addColumn >("cmds"); + QTest::addColumn("success"); + + // success data + { + QList cmds; + cmds << connectToHost01; + QTest::newRow( "simple_ok01" ) << cmds << 1; + } + { + QList cmds; + cmds << connectToHost01; + cmds << login01; + QTest::newRow( "simple_ok02" ) << cmds << 1; + } + { + QList cmds; + cmds << connectToHost01; + cmds << login01; + cmds << close01; + QTest::newRow( "simple_ok03" ) << cmds << 1; + } + { + QList cmds; + cmds << connectToHost01; + cmds << close01; + QTest::newRow( "simple_ok04" ) << cmds << 1; + } + { + QList cmds; + cmds << connectToHost01; + cmds << login01; + cmds << close01; + cmds << connectToHost01; + cmds << login02; + cmds << close01; + QTest::newRow( "connect_twice" ) << cmds << 1; + } + + // error data + { + QList cmds; + cmds << close01; + QTest::newRow( "error01" ) << cmds << 0; + } + { + QList cmds; + cmds << login01; + QTest::newRow( "error02" ) << cmds << 0; + } + { + QList cmds; + cmds << login01; + cmds << close01; + QTest::newRow( "error03" ) << cmds << 0; + } + { + QList cmds; + cmds << connectToHost01; + cmds << login01; + cmds << close01; + cmds << login01; + QTest::newRow( "error04" ) << cmds << 0; + } +} + +void tst_QFtp::commandSequence() +{ + QFETCH( QList, cmds ); + + ftp = newFtp(); + QList::iterator it; + for ( it = cmds.begin(); it != cmds.end(); ++it ) { + switch ( (*it).cmd ) { + case QFtp::ConnectToHost: + { + QVERIFY( (*it).args.count() == 2 ); + uint port; + bool portOk; + port = (*it).args[1].toUInt( &portOk ); + QVERIFY( portOk ); + ids << ftp->connectToHost( (*it).args[0], port ); + } + break; + case QFtp::Login: + QVERIFY( (*it).args.count() == 2 ); + ids << ftp->login( (*it).args[0], (*it).args[1] ); + break; + case QFtp::Close: + QVERIFY( (*it).args.count() == 0 ); + ids << ftp->close(); + break; + default: + QFAIL( "Error in test: unexpected enum value" ); + break; + } + } + + QTestEventLoop::instance().enterLoop( 30 ); + delete ftp; + ftp = 0; + if ( QTestEventLoop::instance().timeout() ) + QFAIL( "Network operation timed out" ); + + QTEST( commandSequence_success, "success" ); +} + +void tst_QFtp::abort_data() +{ + QTest::addColumn("host"); + QTest::addColumn("port"); + QTest::addColumn("file"); + QTest::addColumn("uploadData"); + + QTest::newRow( "get_fluke01" ) << QtNetworkSettings::serverName() << (uint)21 << QString("qtest/bigfile") << QByteArray(); + QTest::newRow( "get_fluke02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("qtest/rfc3252") << QByteArray(); + + // Qt/CE and Symbian test environment has to less memory for this test +#if !defined(Q_OS_WINCE) && !defined(Q_OS_SYMBIAN) + QByteArray bigData( 10*1024*1024, 0 ); +#else + QByteArray bigData( 1*1024*1024, 0 ); +#endif + bigData.fill( 'B' ); + QTest::newRow( "put_fluke01" ) << QtNetworkSettings::serverName() << (uint)21 << QString("qtest/upload/abort_put") << bigData; +} + +void tst_QFtp::abort() +{ + // In case you wonder where the abort() actually happens, look into + // tst_QFtp::dataTransferProgress + // + QFETCH( QString, host ); + QFETCH( uint, port ); + QFETCH( QString, file ); + QFETCH( QByteArray, uploadData ); + + QFtp::Command cmd; + if ( uploadData.size() == 0 ) + cmd = QFtp::Get; + else + cmd = QFtp::Put; + + ftp = newFtp(); + addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) ); + addCommand( QFtp::Login, ftp->login() ); + if ( cmd == QFtp::Get ) + addCommand( cmd, ftp->get( file ) ); + else + addCommand( cmd, ftp->put( uploadData, file ) ); + addCommand( QFtp::Close, ftp->close() ); + + for(int time = 0; time <= uploadData.length() / 30000; time += 30) { + QTestEventLoop::instance().enterLoop( 50 ); + if(ftp->currentCommand() == QFtp::None) + break; + } + delete ftp; + ftp = 0; + if ( QTestEventLoop::instance().timeout() ) + QFAIL( "Network operation timed out" ); + + ResMapIt it = resultMap.find( cmd ); + QVERIFY( it != resultMap.end() ); + // ### how to test the abort? + if ( it.value().success ) { + // The FTP server on fluke is sadly returning a success, even when + // the operation was aborted. So we have to use some heuristics. + if ( host == QtNetworkSettings::serverName() ) { + if ( cmd == QFtp::Get ) { + QVERIFY(bytesDone <= bytesTotal); + } else { + // put commands should always be aborted, since we use really + // big data + QVERIFY( bytesDone != bytesTotal ); + } + } else { + // this could be tested by verifying that no more progress signals are emitted + QVERIFY(bytesDone <= bytesTotal); + } + } else { + QVERIFY( bytesDone != bytesTotal ); + } + + if ( cmd == QFtp::Put ) { + ////////////////////////////////////// + // cleanup (i.e. remove the file) + init(); + ftp = newFtp(); + addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) ); + addCommand( QFtp::Login, ftp->login() ); + addCommand( QFtp::Remove, ftp->remove( file ) ); + addCommand( QFtp::Close, ftp->close() ); + + QTestEventLoop::instance().enterLoop( 30 ); + delete ftp; + ftp = 0; + if ( QTestEventLoop::instance().timeout() ) + QFAIL( "Network operation timed out" ); + + it = resultMap.find( QFtp::Remove ); + QVERIFY( it != resultMap.end() ); + QVERIFY( it.value().success == 1 ); + } +} + +void tst_QFtp::bytesAvailable_data() +{ + QTest::addColumn("host"); + QTest::addColumn("file"); + QTest::addColumn("type"); + QTest::addColumn("bytesAvailFinishedGet"); + QTest::addColumn("bytesAvailFinished"); + QTest::addColumn("bytesAvailDone"); + + QTest::newRow( "fluke01" ) << QtNetworkSettings::serverName() << QString("qtest/bigfile") << 0 << (qlonglong)519240 << (qlonglong)519240 << (qlonglong)519240; + QTest::newRow( "fluke02" ) << QtNetworkSettings::serverName() << QString("qtest/rfc3252") << 0 << (qlonglong)25962 << (qlonglong)25962 << (qlonglong)25962; + + QTest::newRow( "fluke03" ) << QtNetworkSettings::serverName() << QString("qtest/bigfile") << 1 << (qlonglong)519240 << (qlonglong)0 << (qlonglong)0; + QTest::newRow( "fluke04" ) << QtNetworkSettings::serverName() << QString("qtest/rfc3252") << 1 << (qlonglong)25962 << (qlonglong)0 << (qlonglong)0; +} + +void tst_QFtp::bytesAvailable() +{ + QFETCH( QString, host ); + QFETCH( QString, file ); + QFETCH( int, type ); + + ftp = newFtp(); + addCommand( QFtp::ConnectToHost, ftp->connectToHost( host ) ); + addCommand( QFtp::Login, ftp->login() ); + addCommand( QFtp::Get, ftp->get( file ) ); + if ( type != 0 ) + addCommand( QFtp::Close, ftp->close() ); + + QTestEventLoop::instance().enterLoop( 40 ); + if ( QTestEventLoop::instance().timeout() ) + QFAIL( "Network operation timed out" ); + + ResMapIt it = resultMap.find( QFtp::Get ); + QVERIFY( it != resultMap.end() ); + QVERIFY( it.value().success ); + + QFETCH(qlonglong, bytesAvailFinishedGet); + QCOMPARE(bytesAvailable_finishedGet, bytesAvailFinishedGet); + + QFETCH(qlonglong, bytesAvailFinished); + QCOMPARE(bytesAvailable_finished, bytesAvailFinished); + + QFETCH(qlonglong, bytesAvailDone); + QCOMPARE(bytesAvailable_done, bytesAvailDone); + + ftp->readAll(); + QVERIFY( ftp->bytesAvailable() == 0 ); + delete ftp; + ftp = 0; +} + +void tst_QFtp::activeMode() +{ + QFile file("tst_QFtp_activeMode_inittab"); + file.open(QIODevice::ReadWrite); + QFtp ftp; + ftp.setTransferMode(QFtp::Active); + ftp.connectToHost(QtNetworkSettings::serverName(), 21); + ftp.login(); + ftp.list(); + ftp.get("/qtest/rfc3252.txt", &file); + connect(&ftp, SIGNAL(done(bool)), SLOT(activeModeDone(bool))); + QTestEventLoop::instance().enterLoop(900); + QFile::remove("tst_QFtp_activeMode_inittab"); + QVERIFY(done_success == 1); + +} + +void tst_QFtp::activeModeDone(bool error) +{ + done_success = error ? -1 : 1; + QTestEventLoop::instance().exitLoop(); +} + +void tst_QFtp::proxy_data() +{ + QTest::addColumn("host"); + QTest::addColumn("port"); + QTest::addColumn("user"); + QTest::addColumn("password"); + QTest::addColumn("dir"); + QTest::addColumn("success"); + QTest::addColumn("entryNames"); // ### we should rather use a QList here + + QStringList flukeRoot; + flukeRoot << "qtest"; + QStringList flukeQtest; + flukeQtest << "bigfile"; + flukeQtest << "nonASCII"; + flukeQtest << "rfc3252"; + flukeQtest << "rfc3252.txt"; + flukeQtest << "upload"; + + QTest::newRow( "proxy_relPath01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("qtest") << 1 << flukeQtest; + QTest::newRow( "proxy_relPath02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") << QString("qtest") << 1 << flukeQtest; + + QTest::newRow( "proxy_absPath01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("/qtest") << 1 << flukeQtest; + QTest::newRow( "proxy_absPath02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") << QString("/var/ftp/qtest") << 1 << flukeQtest; + + QTest::newRow( "proxy_nonExist01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("foo") << 0 << QStringList(); + QTest::newRow( "proxy_nonExist03" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("/foo") << 0 << QStringList(); +} + +void tst_QFtp::proxy() +{ + QFETCH( QString, host ); + QFETCH( uint, port ); + QFETCH( QString, user ); + QFETCH( QString, password ); + QFETCH( QString, dir ); + + ftp = newFtp(); + addCommand( QFtp::SetProxy, ftp->setProxy( QtNetworkSettings::serverName(), 2121 ) ); + addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) ); + addCommand( QFtp::Login, ftp->login( user, password ) ); + addCommand( QFtp::Cd, ftp->cd( dir ) ); + addCommand( QFtp::List, ftp->list() ); + + QTestEventLoop::instance().enterLoop( 50 ); + + delete ftp; + ftp = 0; + if ( QTestEventLoop::instance().timeout() ) { + QFAIL( "Network operation timed out" ); + } + + ResMapIt it = resultMap.find( QFtp::Cd ); + QVERIFY( it != resultMap.end() ); + QFETCH( int, success ); + QCOMPARE( it.value().success, success ); + QFETCH( QStringList, entryNames ); + QCOMPARE( listInfo_i.count(), entryNames.count() ); + for ( uint i=0; i < (uint) entryNames.count(); i++ ) { + QCOMPARE( listInfo_i[i].name(), entryNames[i] ); + } +} + +void tst_QFtp::binaryAscii() +{ + QString file = "asciifile%1.txt"; + + if(file.contains('%')) + file = file.arg(uniqueExtension); + + QByteArray putData = "a line of text\r\n"; + + init(); + ftp = newFtp(); + addCommand(QFtp::ConnectToHost, ftp->connectToHost(QtNetworkSettings::serverName(), 21)); + addCommand(QFtp::Login, ftp->login("ftptest", "password")); + addCommand(QFtp::Cd, ftp->cd("qtest/upload")); + addCommand(QFtp::Put, ftp->put(putData, file, QFtp::Ascii)); + addCommand(QFtp::Close, ftp->close()); + + QTestEventLoop::instance().enterLoop( 30 ); + delete ftp; + ftp = 0; + if ( QTestEventLoop::instance().timeout() ) + QFAIL( "Network operation timed out" ); + + ResMapIt it = resultMap.find(QFtp::Put); + QVERIFY(it != resultMap.end()); + QVERIFY(it.value().success); + + QByteArray getData; + QBuffer getBuf(&getData); + getBuf.open(QBuffer::WriteOnly); + + init(); + ftp = newFtp(); + addCommand(QFtp::ConnectToHost, ftp->connectToHost(QtNetworkSettings::serverName(), 21)); + addCommand(QFtp::Login, ftp->login("ftptest", "password")); + addCommand(QFtp::Cd, ftp->cd("qtest/upload")); + addCommand(QFtp::Get, ftp->get(file, &getBuf, QFtp::Binary)); + addCommand(QFtp::Close, ftp->close()); + + QTestEventLoop::instance().enterLoop( 30 ); + delete ftp; + ftp = 0; + if ( QTestEventLoop::instance().timeout() ) + QFAIL( "Network operation timed out" ); + + ResMapIt it2 = resultMap.find(QFtp::Get); + QVERIFY(it2 != resultMap.end()); + QVERIFY(it2.value().success); + // most modern ftp servers leave the file as it is by default + // (and do not remove the windows line ending), the -1 below could be + // deleted in the future + QVERIFY(getData.size() == putData.size()-1); + ////////////////////////////////////////////////////////////////// + // cleanup (i.e. remove the file) -- this also tests the remove command + init(); + ftp = newFtp(); + addCommand(QFtp::ConnectToHost, ftp->connectToHost(QtNetworkSettings::serverName(), 21)); + addCommand(QFtp::Login, ftp->login("ftptest", "password")); + addCommand(QFtp::Cd, ftp->cd("qtest/upload")); + addCommand(QFtp::Remove, ftp->remove(file)); + addCommand(QFtp::Close, ftp->close()); + + QTestEventLoop::instance().enterLoop( 30 ); + delete ftp; + ftp = 0; + if ( QTestEventLoop::instance().timeout() ) + QFAIL( "Network operation timed out" ); + + it = resultMap.find( QFtp::Remove ); + QVERIFY( it != resultMap.end() ); + QCOMPARE( it.value().success, 1 ); + + QVERIFY(!fileExists(QtNetworkSettings::serverName(), 21, "ftptest", "password", file)); +} + + +// test QFtp::currentId() and QFtp::currentCommand() +#define CURRENTCOMMAND_TEST \ +{ \ + ResMapIt it; \ + for ( it = resultMap.begin(); it != resultMap.end(); ++it ) { \ + if ( it.value().id == ftp->currentId() ) { \ + QVERIFY( it.key() == ftp->currentCommand() ); \ + } \ +} \ +} + +void tst_QFtp::commandStarted( int id ) +{ +#if defined( DUMP_SIGNALS ) + qDebug( "%d:commandStarted( %d )", ftp->currentId(), id ); +#endif + // make sure that the commandStarted and commandFinished are nested correctly + QVERIFY( current_id == 0 ); + current_id = id; + + QVERIFY( !ids.isEmpty() ); + QVERIFY( ids.first() == id ); + if ( ids.count() > 1 ) { + QVERIFY( ftp->hasPendingCommands() ); + } else { + QVERIFY( !ftp->hasPendingCommands() ); + } + + QVERIFY( ftp->currentId() == id ); + QVERIFY( cur_state == ftp->state() ); + CURRENTCOMMAND_TEST; + + QVERIFY( ftp->error() == QFtp::NoError ); +} + +void tst_QFtp::commandFinished( int id, bool error ) +{ +#if defined( DUMP_SIGNALS ) + qDebug( "%d:commandFinished( %d, %d ) -- errorString: '%s'", + ftp->currentId(), id, (int)error, ftp->errorString().toLatin1().constData() ); +#endif + if ( ftp->currentCommand() == QFtp::Get ) { + bytesAvailable_finishedGet = ftp->bytesAvailable(); + } + bytesAvailable_finished = ftp->bytesAvailable(); + + // make sure that the commandStarted and commandFinished are nested correctly + QVERIFY( current_id == id ); + current_id = 0; + + QVERIFY( !ids.isEmpty() ); + QVERIFY( ids.first() == id ); + if ( !error && ids.count() > 1) { + QVERIFY( ftp->hasPendingCommands() ); + } else { + QVERIFY( !ftp->hasPendingCommands() ); + } + if ( error ) { + QVERIFY( ftp->error() != QFtp::NoError ); + ids.clear(); + } else { + QVERIFY( ftp->error() == QFtp::NoError ); + ids.pop_front(); + } + + QVERIFY( ftp->currentId() == id ); + QVERIFY( cur_state == ftp->state() ); + CURRENTCOMMAND_TEST; + + if ( QTest::currentTestFunction() != QLatin1String("commandSequence") ) { + ResMapIt it = resultMap.find( ftp->currentCommand() ); + QVERIFY( it != resultMap.end() ); + QVERIFY( it.value().success == -1 ); + if ( error ) + it.value().success = 0; + else + it.value().success = 1; + } +} + +void tst_QFtp::done( bool error ) +{ +#if defined( DUMP_SIGNALS ) + qDebug( "%d:done( %d )", ftp->currentId(), (int)error ); +#endif + bytesAvailable_done = ftp->bytesAvailable(); + + QVERIFY( ftp->currentId() == 0 ); + QVERIFY( current_id == 0 ); + QVERIFY( ids.isEmpty() ); + QVERIFY( cur_state == ftp->state() ); + QVERIFY( !ftp->hasPendingCommands() ); + + if ( QTest::currentTestFunction() == QLatin1String("commandSequence") ) { + QVERIFY( commandSequence_success == -1 ); + if ( error ) + commandSequence_success = 0; + else + commandSequence_success = 1; + } + QVERIFY( done_success == -1 ); + if ( error ) { + QVERIFY( ftp->error() != QFtp::NoError ); + done_success = 0; + } else { + QVERIFY( ftp->error() == QFtp::NoError ); + done_success = 1; + } + QTestEventLoop::instance().exitLoop(); +} + +void tst_QFtp::stateChanged( int state ) +{ +#if defined( DUMP_SIGNALS ) + qDebug( "%d: stateChanged( %d )", ftp->currentId(), state ); +#endif + QCOMPARE( ftp->currentId(), current_id ); + CURRENTCOMMAND_TEST; + + QVERIFY( state != cur_state ); + QCOMPARE( state, (int)ftp->state() ); + if ( state != QFtp::Unconnected ) { + // make sure that the states are always emitted in the right order (for + // this, we assume an ordering on the enum values, which they have at + // the moment) + QVERIFY( cur_state < state ); + + // make sure that state changes are only emitted in response to certain + // commands + switch ( state ) { + case QFtp::HostLookup: + case QFtp::Connecting: + case QFtp::Connected: + QCOMPARE( (int)ftp->currentCommand(), (int)QFtp::ConnectToHost ); + break; + case QFtp::LoggedIn: + QCOMPARE( (int)ftp->currentCommand(), (int)QFtp::Login ); + break; + case QFtp::Closing: + QCOMPARE( (int)ftp->currentCommand(), (int)QFtp::Close ); + break; + default: + QWARN( QString("Unexpected state '%1'").arg(state).toLatin1().constData() ); + break; + } + } + cur_state = state; + + if ( QTest::currentTestFunction() == QLatin1String("connectToHost") ) { + switch ( state ) { + case QFtp::HostLookup: + case QFtp::Connecting: + case QFtp::LoggedIn: + case QFtp::Closing: + // ignore + break; + case QFtp::Connected: + case QFtp::Unconnected: + QVERIFY( connectToHost_state == -1 ); + connectToHost_state = state; + break; + default: + QWARN( QString("Unknown state '%1'").arg(state).toLatin1().constData() ); + break; + } + } else if ( QTest::currentTestFunction() == QLatin1String("close") ) { + ResMapIt it = resultMap.find( QFtp::Close ); + if ( it!=resultMap.end() && ftp->currentId()==it.value().id ) { + if ( state == QFtp::Closing ) { + QVERIFY( close_state == -1 ); + close_state = state; + } else if ( state == QFtp::Unconnected ) { + QVERIFY( close_state == QFtp::Closing ); + close_state = state; + } + } + } else if ( QTest::currentTestFunction() == QLatin1String("login") ) { + ResMapIt it = resultMap.find( QFtp::Login ); + if ( it!=resultMap.end() && ftp->currentId()==it.value().id ) { + if ( state == QFtp::LoggedIn ) { + QVERIFY( login_state == -1 ); + login_state = state; + } + } + } +} + +void tst_QFtp::listInfo( const QUrlInfo &i ) +{ +#if defined( DUMP_SIGNALS ) + qDebug( "%d: listInfo( %s )", ftp->currentId(), i.name().toLatin1().constData() ); +#endif + QCOMPARE( ftp->currentId(), current_id ); + if ( ids.count() > 1 ) { + QVERIFY( ftp->hasPendingCommands() ); + } else { + QVERIFY( !ftp->hasPendingCommands() ); + } + QVERIFY( cur_state == ftp->state() ); + CURRENTCOMMAND_TEST; + + if ( QTest::currentTestFunction()==QLatin1String("list") || QTest::currentTestFunction()==QLatin1String("cd") || QTest::currentTestFunction()==QLatin1String("proxy") || inFileDirExistsFunction ) { + ResMapIt it = resultMap.find( QFtp::List ); + QVERIFY( it != resultMap.end() ); + QVERIFY( ftp->currentId() == it.value().id ); + listInfo_i << i; + } +} + +void tst_QFtp::readyRead() +{ +#if defined( DUMP_SIGNALS ) + qDebug( "%d: readyRead(), bytesAvailable == %lu", ftp->currentId(), ftp->bytesAvailable() ); +#endif + QCOMPARE( ftp->currentId(), current_id ); + if ( ids.count() > 1 ) { + QVERIFY( ftp->hasPendingCommands() ); + } else { + QVERIFY( !ftp->hasPendingCommands() ); + } + QVERIFY( cur_state == ftp->state() ); + CURRENTCOMMAND_TEST; + + if ( QTest::currentTestFunction() != QLatin1String("bytesAvailable") ) { + int oldSize = newData_ba.size(); + qlonglong bytesAvail = ftp->bytesAvailable(); + QByteArray ba = ftp->readAll(); + QVERIFY( ba.size() == (int) bytesAvail ); + newData_ba.resize( oldSize + ba.size() ); + memcpy( newData_ba.data()+oldSize, ba.data(), ba.size() ); + + if ( bytesTotal != -1 ) { + QVERIFY( (int)newData_ba.size() <= bytesTotal ); + } + QVERIFY( (int)newData_ba.size() == bytesDone ); + } +} + +void tst_QFtp::dataTransferProgress( qint64 done, qint64 total ) +{ +#if defined( DUMP_SIGNALS ) + qDebug( "%d: dataTransferProgress( %lli, %lli )", ftp->currentId(), done, total ); +#endif + QCOMPARE( ftp->currentId(), current_id ); + if ( ids.count() > 1 ) { + QVERIFY( ftp->hasPendingCommands() ); + } else { + QVERIFY( !ftp->hasPendingCommands() ); + } + QVERIFY( cur_state == ftp->state() ); + CURRENTCOMMAND_TEST; + + if ( bytesTotal == bytesTotal_init ) { + bytesTotal = total; + } else { + QVERIFY( bytesTotal == total ); + } + + QVERIFY( bytesTotal != bytesTotal_init ); + QVERIFY( bytesDone <= done ); + bytesDone = done; + if ( bytesTotal != -1 ) { + QVERIFY( bytesDone <= bytesTotal ); + } + + if ( QTest::currentTestFunction() == QLatin1String("abort") ) { + // ### it would be nice if we could specify in our testdata when to do + // the abort + if ( done >= total/100000 ) { + if ( ids.count() != 1 ) { + // do abort only once + int tmpId = ids.first(); + ids.clear(); + ids << tmpId; + ftp->abort(); + } + } + } +} + + +QFtp *tst_QFtp::newFtp() +{ + QFtp *nFtp = new QFtp( this ); +#ifndef QT_NO_BEARERMANAGEMENT + if (networkSessionExplicit) { + nFtp->setProperty("_q_networksession", QVariant::fromValue(networkSessionExplicit)); + } +#endif + connect( nFtp, SIGNAL(commandStarted(int)), + SLOT(commandStarted(int)) ); + connect( nFtp, SIGNAL(commandFinished(int,bool)), + SLOT(commandFinished(int,bool)) ); + connect( nFtp, SIGNAL(done(bool)), + SLOT(done(bool)) ); + connect( nFtp, SIGNAL(stateChanged(int)), + SLOT(stateChanged(int)) ); + connect( nFtp, SIGNAL(listInfo(const QUrlInfo&)), + SLOT(listInfo(const QUrlInfo&)) ); + connect( nFtp, SIGNAL(readyRead()), + SLOT(readyRead()) ); + connect( nFtp, SIGNAL(dataTransferProgress(qint64, qint64)), + SLOT(dataTransferProgress(qint64, qint64)) ); + + return nFtp; +} + +void tst_QFtp::addCommand( QFtp::Command cmd, int id ) +{ + ids << id; + CommandResult res; + res.id = id; + res.success = -1; + resultMap[ cmd ] = res; +} + +bool tst_QFtp::fileExists( const QString &host, quint16 port, const QString &user, const QString &password, const QString &file, const QString &cdDir ) +{ + init(); + ftp = newFtp(); + // ### make these tests work + if (ftp->currentId() != 0) { + qWarning("ftp->currentId() != 0"); + return FALSE; + } + + if (ftp->state() != QFtp::Unconnected) { + qWarning("ftp->state() != QFtp::Unconnected"); + return FALSE; + } + + addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) ); + addCommand( QFtp::Login, ftp->login( user, password ) ); + if ( !cdDir.isNull() ) + addCommand( QFtp::Cd, ftp->cd( cdDir ) ); + addCommand( QFtp::List, ftp->list( file ) ); + addCommand( QFtp::Close, ftp->close() ); + + inFileDirExistsFunction = TRUE; + QTestEventLoop::instance().enterLoop( 30 ); + delete ftp; + ftp = 0; + if ( QTestEventLoop::instance().timeout() ) { + // ### make this test work + qWarning("tst_QFtp::fileExists: Network operation timed out"); + return FALSE; + } + inFileDirExistsFunction = FALSE; + + ResMapIt it = resultMap.find( QFtp::ConnectToHost ); + // ### make these tests work + if (it == resultMap.end()) { + qWarning("it != resultMap.end()"); + return FALSE; + } + + if (it.value().success == -1) { + qWarning("it.value().success != -1"); + return FALSE; + } + + if ( it.value().success == 1 ) { + for ( uint i=0; i < (uint) listInfo_i.count(); i++ ) { + if ( QFileInfo(listInfo_i[i].name()).fileName() == QFileInfo(file).fileName() ) + return TRUE; + } + } + + //this is not a good warning considering sometime this function is used to test that a file does not exist + //qWarning("file doesn't exist"); + return FALSE; +} + +bool tst_QFtp::dirExists( const QString &host, quint16 port, const QString &user, const QString &password, const QString &cdDir, const QString &dirToCreate ) +{ + init(); + ftp = newFtp(); + // ### make these tests work + // QCOMPARE( ftp->currentId(), 0 ); + // QCOMPARE( (int)ftp->state(), (int)QFtp::Unconnected ); + + addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) ); + addCommand( QFtp::Login, ftp->login( user, password ) ); + if ( dirToCreate.startsWith( "/" ) ) + addCommand( QFtp::Cd, ftp->cd( dirToCreate ) ); + else + addCommand( QFtp::Cd, ftp->cd( cdDir + "/" + dirToCreate ) ); + addCommand( QFtp::Close, ftp->close() ); + + inFileDirExistsFunction = TRUE; + QTestEventLoop::instance().enterLoop( 30 ); + delete ftp; + ftp = 0; + if ( QTestEventLoop::instance().timeout() ) { + // ### make this test work + // QFAIL( "Network operation timed out" ); + qWarning("tst_QFtp::dirExists: Network operation timed out"); + return FALSE; + } + inFileDirExistsFunction = FALSE; + + ResMapIt it = resultMap.find( QFtp::Cd ); + // ### make these tests work + // QVERIFY( it != resultMap.end() ); + // QVERIFY( it.value().success != -1 ); + return it.value().success == 1; +} + +void tst_QFtp::doneSignal() +{ + QFtp ftp; + QSignalSpy spy(&ftp, SIGNAL(done(bool))); + + ftp.connectToHost(QtNetworkSettings::serverName()); + ftp.login("anonymous"); + ftp.list(); + ftp.close(); + + done_success = 0; + connect(&ftp, SIGNAL(done(bool)), &(QTestEventLoop::instance()), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(61); + if (QTestEventLoop::instance().timeout()) + QFAIL("Network operation timed out"); + + QTest::qWait(200); + + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.first().first().toBool(), false); +} + +void tst_QFtp::queueMoreCommandsInDoneSlot() +{ + QSKIP("Task 127050 && 113966", SkipSingle); + + QFtp ftp; + QSignalSpy doneSpy(&ftp, SIGNAL(done(bool))); + QSignalSpy commandFinishedSpy(&ftp, SIGNAL(commandFinished(int, bool))); + + this->ftp = &ftp; + connect(&ftp, SIGNAL(done(bool)), this, SLOT(cdUpSlot(bool))); + + ftp.connectToHost("ftp.qt.nokia.com"); + ftp.login(); + ftp.cd("qt"); + ftp.rmdir("qtest-removedir-noexist"); + + while ( ftp.hasPendingCommands() || ftp.currentCommand() != QFtp::None ) { + QCoreApplication::instance()->processEvents(QEventLoop::AllEvents + | QEventLoop::WaitForMoreEvents); + } + + QCOMPARE(doneSpy.count(), 2); + QCOMPARE(doneSpy.first().first().toBool(), true); + QCOMPARE(doneSpy.last().first().toBool(), false); + + QCOMPARE(commandFinishedSpy.count(), 6); + int firstId = commandFinishedSpy.at(0).at(0).toInt(); + QCOMPARE(commandFinishedSpy.at(0).at(1).toBool(), false); + QCOMPARE(commandFinishedSpy.at(1).at(0).toInt(), firstId + 1); + QCOMPARE(commandFinishedSpy.at(1).at(1).toBool(), false); + QCOMPARE(commandFinishedSpy.at(2).at(0).toInt(), firstId + 2); + QCOMPARE(commandFinishedSpy.at(2).at(1).toBool(), false); + QCOMPARE(commandFinishedSpy.at(3).at(0).toInt(), firstId + 3); + QCOMPARE(commandFinishedSpy.at(3).at(1).toBool(), true); + QCOMPARE(commandFinishedSpy.at(4).at(0).toInt(), firstId + 4); + QCOMPARE(commandFinishedSpy.at(4).at(1).toBool(), false); + QCOMPARE(commandFinishedSpy.at(5).at(0).toInt(), firstId + 5); + QCOMPARE(commandFinishedSpy.at(5).at(1).toBool(), false); + + this->ftp = 0; +} + +void tst_QFtp::cdUpSlot(bool error) +{ + if (error) { + ftp->cd(".."); + ftp->cd("qt"); + } +} + +void tst_QFtp::qtbug7359Crash() +{ + QFtp ftp; + ftp.connectToHost("127.0.0.1"); + + QTime t; + int elapsed; + + t.start(); + while ((elapsed = t.elapsed()) < 200) + QCoreApplication::processEvents(QEventLoop::AllEvents, 200 - elapsed); + + ftp.close(); + t.restart(); + while ((elapsed = t.elapsed()) < 1000) + QCoreApplication::processEvents(QEventLoop::AllEvents, 1000 - elapsed); + + ftp.connectToHost("127.0.0.1"); + + t.restart(); + while ((elapsed = t.elapsed()) < 2000) + QCoreApplication::processEvents(QEventLoop::AllEvents, 2000 - elapsed); +} + +QTEST_MAIN(tst_QFtp) + +#include "tst_qftp.moc" diff --git a/tests/auto/network/access/qhttp/.gitattributes b/tests/auto/network/access/qhttp/.gitattributes new file mode 100644 index 0000000000..e04709aa2e --- /dev/null +++ b/tests/auto/network/access/qhttp/.gitattributes @@ -0,0 +1 @@ +rfc3252.txt -crlf diff --git a/tests/auto/network/access/qhttp/.gitignore b/tests/auto/network/access/qhttp/.gitignore new file mode 100644 index 0000000000..00c1f492cd --- /dev/null +++ b/tests/auto/network/access/qhttp/.gitignore @@ -0,0 +1 @@ +tst_qhttp diff --git a/tests/auto/network/access/qhttp/dummyserver.h b/tests/auto/network/access/qhttp/dummyserver.h new file mode 100644 index 0000000000..7c14ae2f14 --- /dev/null +++ b/tests/auto/network/access/qhttp/dummyserver.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +// Use if you need + +class DummyHttpServer : public QTcpServer +{ + Q_OBJECT +public: + DummyHttpServer() : phase(Header) + { listen(); } + +protected: + enum { + Header, + Data1, + Data2, + Close + } phase; + void incomingConnection(int socketDescriptor) + { + QSslSocket *socket = new QSslSocket(this); + socket->setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState); + socket->ignoreSslErrors(); + socket->startServerEncryption(); + connect(socket, SIGNAL(readyRead()), SLOT(handleReadyRead())); + } + +public slots: + void handleReadyRead() + { + QTcpSocket *socket = static_cast(sender()); + socket->readAll(); + if (phase != Header) + return; + + phase = Data1; + static const char header[] = + "HTTP/1.0 200 OK\r\n" + "Date: Fri, 07 Sep 2007 12:33:18 GMT\r\n" + "Server: Apache\r\n" + "Expires:\r\n" + "Cache-Control:\r\n" + "Pragma:\r\n" + "Last-Modified: Thu, 06 Sep 2007 08:52:06 +0000\r\n" + "Etag: a700f59a6ccb1ad39af68d998aa36fb1\r\n" + "Vary: Accept-Encoding\r\n" + "Content-Length: 6560\r\n" + "Connection: close\r\n" + "Content-Type: text/html; charset=utf-8\r\n" + "\r\n"; + + + socket->write(header, sizeof header - 1); + connect(socket, SIGNAL(bytesWritten(qint64)), SLOT(handleBytesWritten()), Qt::QueuedConnection); + } + + void handleBytesWritten() + { + QTcpSocket *socket = static_cast(sender()); + if (socket->bytesToWrite() != 0) + return; + + if (phase == Data1) { + QByteArray data(4096, 'a'); + socket->write(data); + phase = Data2; + } else if (phase == Data2) { + QByteArray data(2464, 'a'); + socket->write(data); + phase = Close; + } else { + //socket->disconnectFromHost(); + //socket->deleteLater(); + } + } +}; diff --git a/tests/auto/network/access/qhttp/qhttp.pro b/tests/auto/network/access/qhttp/qhttp.pro new file mode 100644 index 0000000000..f01f60f3d7 --- /dev/null +++ b/tests/auto/network/access/qhttp/qhttp.pro @@ -0,0 +1,31 @@ +load(qttest_p4) +SOURCES += tst_qhttp.cpp + + +QT = core network + +wince*: { + webFiles.files = webserver/* + webFiles.path = webserver + cgi.files = webserver/cgi-bin/* + cgi.path = webserver/cgi-bin + addFiles.files = rfc3252.txt trolltech + addFiles.path = . + DEPLOYMENT += addFiles webFiles cgi + DEFINES += SRCDIR=\\\"\\\" +} else:symbian { + webFiles.files = webserver/* + webFiles.path = webserver + cgi.files = webserver/cgi-bin/* + cgi.path = webserver/cgi-bin + addFiles.files = rfc3252.txt trolltech + addFiles.path = . + DEPLOYMENT += addFiles webFiles cgi + TARGET.CAPABILITY = NetworkServices +} else:vxworks*: { + DEFINES += SRCDIR=\\\"\\\" +} else { + DEFINES += SRCDIR=\\\"$$PWD/\\\" +} + +CONFIG+=insignificant_test diff --git a/tests/auto/network/access/qhttp/rfc3252.txt b/tests/auto/network/access/qhttp/rfc3252.txt new file mode 100644 index 0000000000..b80c61bf0a --- /dev/null +++ b/tests/auto/network/access/qhttp/rfc3252.txt @@ -0,0 +1,899 @@ + + + + + + +Network Working Group H. Kennedy +Request for Comments: 3252 Mimezine +Category: Informational 1 April 2002 + + + Binary Lexical Octet Ad-hoc Transport + +Status of this Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2002). All Rights Reserved. + +Abstract + + This document defines a reformulation of IP and two transport layer + protocols (TCP and UDP) as XML applications. + +1. Introduction + +1.1. Overview + + This document describes the Binary Lexical Octet Ad-hoc Transport + (BLOAT): a reformulation of a widely-deployed network-layer protocol + (IP [RFC791]), and two associated transport layer protocols (TCP + [RFC793] and UDP [RFC768]) as XML [XML] applications. It also + describes methods for transporting BLOAT over Ethernet and IEEE 802 + networks as well as encapsulating BLOAT in IP for gatewaying BLOAT + across the public Internet. + +1.2. Motivation + + The wild popularity of XML as a basis for application-level protocols + such as the Blocks Extensible Exchange Protocol [RFC3080], the Simple + Object Access Protocol [SOAP], and Jabber [JABBER] prompted + investigation into the possibility of extending the use of XML in the + protocol stack. Using XML at both the transport and network layer in + addition to the application layer would provide for an amazing amount + of power and flexibility while removing dependencies on proprietary + and hard-to-understand binary protocols. This protocol unification + would also allow applications to use a single XML parser for all + aspects of their operation, eliminating developer time spent figuring + out the intricacies of each new protocol, and moving the hard work of + + + + +Kennedy Informational [Page 1] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + parsing to the XML toolset. The use of XML also mitigates concerns + over "network vs. host" byte ordering which is at the root of many + network application bugs. + +1.3. Relation to Existing Protocols + + The reformulations specified in this RFC follow as closely as + possible the spirit of the RFCs on which they are based, and so MAY + contain elements or attributes that would not be needed in a pure + reworking (e.g. length attributes, which are implicit in XML.) + + The layering of network and transport protocols are maintained in + this RFC despite the optimizations that could be made if the line + were somewhat blurred (i.e. merging TCP and IP into a single, larger + element in the DTD) in order to foster future use of this protocol as + a basis for reformulating other protocols (such as ICMP.) + + Other than the encoding, the behavioral aspects of each of the + existing protocols remain unchanged. Routing, address spaces, TCP + congestion control, etc. behave as specified in the extant standards. + Adapting to new standards and experimental algorithm heuristics for + improving performance will become much easier once the move to BLOAT + has been completed. + +1.4. Requirement Levels + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in BCP 14, RFC 2119 + [RFC2119]. + +2. IPoXML + + This protocol MUST be implemented to be compliant with this RFC. + IPoXML is the root protocol REQUIRED for effective use of TCPoXML + (section 3.) and higher-level application protocols. + + The DTD for this document type can be found in section 7.1. + + The routing of IPoXML can be easily implemented on hosts with an XML + parser, as the regular structure lends itself handily to parsing and + validation of the document/datagram and then processing the + destination address, TTL, and checksum before sending it on to its + next-hop. + + The reformulation of IPv4 was chosen over IPv6 [RFC2460] due to the + wider deployment of IPv4 and the fact that implementing IPv6 as XML + would have exceeded the 1500 byte Ethernet MTU. + + + +Kennedy Informational [Page 2] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + All BLOAT implementations MUST use - and specify - the UTF-8 encoding + of RFC 2279 [RFC2279]. All BLOAT document/datagrams MUST be well- + formed and include the XMLDecl. + +2.1. IP Description + + A number of items have changed (for the better) from the original IP + specification. Bit-masks, where present have been converted into + human-readable values. IP addresses are listed in their dotted- + decimal notation [RFC1123]. Length and checksum values are present + as decimal integers. + + To calculate the length and checksum fields of the IP element, a + canonicalized form of the element MUST be used. The canonical form + SHALL have no whitespace (including newline characters) between + elements and only one space character between attributes. There + SHALL NOT be a space following the last attribute in an element. + + An iterative method SHOULD be used to calculate checksums, as the + length field will vary based on the size of the checksum. + + The payload element bears special attention. Due to the character + set restrictions of XML, the payload of IP datagrams (which MAY + contain arbitrary data) MUST be encoded for transport. This RFC + REQUIRES the contents of the payload to be encoded in the base-64 + encoding of RFC 2045 [RFC2045], but removes the requirement that the + encoded output MUST be wrapped on 76-character lines. + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 3] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +2.2. Example Datagram + + The following is an example IPoXML datagram with an empty payload: + + + + +
+ + + + + + + + + + + + + + + +
+ + +
+ +3. TCPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.2. + +3.1. TCP Description + + A number of items have changed from the original TCP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + To calculate the length and checksum fields of the TCP element, a + canonicalized form of the element MUST be used as in section 2.1. + + An iterative method SHOULD be used to calculate checksums as in + section 2.1. + + The payload element MUST be encoded as in section 2.1. + + + +Kennedy Informational [Page 4] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + The TCP offset element was expanded to a maximum of 255 from 16 to + allow for the increased size of the header in XML. + + TCPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +3.2. Example Datagram + + The following is an example TCPoXML datagram with an empty payload: + + + + + + + + + + + + + + + + + + + + + + + + +4. UDPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.3. + +4.1. UDP Description + + A number of items have changed from the original UDP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + + + + + + +Kennedy Informational [Page 5] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + To calculate the length and checksum fields of the UDP element, a + canonicalized form of the element MUST be used as in section 2.1. An + iterative method SHOULD be used to calculate checksums as in section + 2.1. + + The payload element MUST be encoded as in section 2.1. + + UDPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +4.2. Example Datagram + + The following is an example UDPoXML datagram with an empty payload: + + + + + + + + + + + + + + +5. Network Transport + + This document provides for the transmission of BLOAT datagrams over + two common families of physical layer transport. Future RFCs will + address additional transports as routing vendors catch up to the + specification, and we begin to see BLOAT routed across the Internet + backbone. + +5.1. Ethernet + + BLOAT is encapsulated in Ethernet datagrams as in [RFC894] with the + exception that the type field of the Ethernet frame MUST contain the + value 0xBEEF. The first 5 octets of the Ethernet frame payload will + be 0x3c 3f 78 6d 6c (" + --> + + + + +Kennedy Informational [Page 7] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 9] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 10] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + +7.2. TCPoXML DTD + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 11] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 12] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + +7.3. UDPoXML DTD + + + + + + + + + + + + + + + +Kennedy Informational [Page 13] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +8. Security Considerations + + XML, as a subset of SGML, has the same security considerations as + specified in SGML Media Types [RFC1874]. Security considerations + that apply to IP, TCP and UDP also likely apply to BLOAT as it does + not attempt to correct for issues not related to message format. + +9. References + + [JABBER] Miller, J., "Jabber", draft-miller-jabber-00.txt, + February 2002. (Work in Progress) + + [RFC768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, + August 1980. + + [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791, + September 1981. + + [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC + 793, September 1981. + + [RFC894] Hornig, C., "Standard for the Transmission of IP + Datagrams over Ethernet Networks.", RFC 894, April 1984. + + [RFC1042] Postel, J. and J. Reynolds, "Standard for the + Transmission of IP Datagrams Over IEEE 802 Networks", STD + 43, RFC 1042, February 1988. + + [RFC1123] Braden, R., "Requirements for Internet Hosts - + Application and Support", RFC 1123, October 1989. + + [RFC1874] Levinson, E., "SGML Media Types", RFC 1874, December + 1995. + + [RFC2003] Perkins, C., "IP Encapsulation within IP", RFC 2003, + October 1996. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", RFC 2279, January 1998. + + + + + +Kennedy Informational [Page 14] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6 + (IPv6) Specification", RFC 2460, December 1998. + + [RFC3080] Rose, M., "The Blocks Extensible Exchange Protocol Core", + RFC 3080, March 2001. + + [SOAP] Box, D., Ehnebuske, D., Kakivaya, G., Layman, A., + Mendelsohn, N., Nielsen, H. F., Thatte, S. Winer, D., + "Simple Object Access Protocol (SOAP) 1.1" World Wide Web + Consortium Note, May 2000 http://www.w3.org/TR/SOAP/ + + [XML] Bray, T., Paoli, J., Sperberg-McQueen, C. M., "Extensible + Markup Language (XML)" World Wide Web Consortium + Recommendation REC- xml-19980210. + http://www.w3.org/TR/1998/REC-xml-19980210 + +10. Author's Address + + Hugh Kennedy + Mimezine + 1060 West Addison + Chicago, IL 60613 + USA + + EMail: kennedyh@engin.umich.edu + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 15] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +11. Full Copyright Statement + + Copyright (C) The Internet Society (2002). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 16] + diff --git a/tests/auto/network/access/qhttp/trolltech b/tests/auto/network/access/qhttp/trolltech new file mode 100644 index 0000000000..c155360e8d --- /dev/null +++ b/tests/auto/network/access/qhttp/trolltech @@ -0,0 +1,8 @@ + + + Test + + +

Test

+ + diff --git a/tests/auto/network/access/qhttp/tst_qhttp.cpp b/tests/auto/network/access/qhttp/tst_qhttp.cpp new file mode 100644 index 0000000000..8875232714 --- /dev/null +++ b/tests/auto/network/access/qhttp/tst_qhttp.cpp @@ -0,0 +1,1576 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef QT_NO_OPENSSL +# include +#endif + +#include "../../../network-settings.h" + +//TESTED_CLASS= +//TESTED_FILES= + +#ifdef Q_OS_SYMBIAN +// In Symbian OS test data is located in applications private dir +// And underlying Open C have application private dir in default search path +#define SRCDIR "" +#endif + +Q_DECLARE_METATYPE(QHttpResponseHeader) + +class tst_QHttp : public QObject +{ + Q_OBJECT + +public: + tst_QHttp(); + virtual ~tst_QHttp(); + + +public slots: + void initTestCase_data(); + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); +private slots: + void constructing(); + void invalidRequests(); + void get_data(); + void get(); + void head_data(); + void head(); + void post_data(); + void post(); + void request_data(); + void request(); + void authorization_data(); + void authorization(); + void proxy_data(); + void proxy(); + void proxy2(); + void proxy3(); + void postAuthNtlm(); + void proxyAndSsl(); + void cachingProxyAndSsl(); + void reconnect(); + void setSocket(); + void unexpectedRemoteClose(); + void pctEncodedPath(); + void caseInsensitiveKeys(); + void emptyBodyInReply(); + void abortInReadyRead(); + void abortInResponseHeaderReceived(); + void nestedEventLoop(); + void connectionClose(); + +protected slots: + void stateChanged( int ); + void responseHeaderReceived( const QHttpResponseHeader & ); + void readyRead( const QHttpResponseHeader& ); + void dataSendProgress( int, int ); + void dataReadProgress( int , int ); + + void requestStarted( int ); + void requestFinished( int, bool ); + void done( bool ); + + void reconnect_state(int state); + void proxy2_slot(); + void nestedEventLoop_slot(int id); + + void abortSender(); + void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth); + +private: + QHttp *newHttp(bool withAuth = false); + void addRequest( QHttpRequestHeader, int ); + bool headerAreEqual( const QHttpHeader&, const QHttpHeader& ); + + QHttp *http; + + struct RequestResult + { + QHttpRequestHeader req; + QHttpResponseHeader resp; + int success; + }; + QMap< int, RequestResult > resultMap; + typedef QMap::Iterator ResMapIt; + QList ids; // helper to make sure that all expected signals are emitted + + int current_id; + int cur_state; + int done_success; + + QByteArray readyRead_ba; + + int bytesTotalSend; + int bytesDoneSend; + int bytesTotalRead; + int bytesDoneRead; + + int getId; + int headId; + int postId; + + int reconnect_state_connect_count; + + bool connectionWithAuth; + bool proxyAuthCalled; +}; + +class ClosingServer: public QTcpServer +{ + Q_OBJECT +public: + ClosingServer() + { + connect(this, SIGNAL(newConnection()), SLOT(handleConnection())); + listen(); + } + +public slots: + void handleConnection() + { + delete nextPendingConnection(); + } +}; + +//#define DUMP_SIGNALS + +const int bytesTotal_init = -10; +const int bytesDone_init = -10; + +tst_QHttp::tst_QHttp() +{ + Q_SET_DEFAULT_IAP +} + +tst_QHttp::~tst_QHttp() +{ +} + +void tst_QHttp::initTestCase_data() +{ + QTest::addColumn("setProxy"); + QTest::addColumn("proxyType"); + + QTest::newRow("WithoutProxy") << false << 0; + QTest::newRow("WithSocks5Proxy") << true << int(QNetworkProxy::Socks5Proxy); +} + +void tst_QHttp::initTestCase() +{ +} + +void tst_QHttp::cleanupTestCase() +{ +} + +void tst_QHttp::init() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) { + QFETCH_GLOBAL(int, proxyType); + if (proxyType == QNetworkProxy::Socks5Proxy) { + QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080)); + } + } + + http = 0; + + resultMap.clear(); + ids.clear(); + + current_id = 0; + cur_state = QHttp::Unconnected; + done_success = -1; + + readyRead_ba = QByteArray(); + + getId = -1; + headId = -1; + postId = -1; +} + +void tst_QHttp::cleanup() +{ + delete http; + http = 0; + + QCoreApplication::processEvents(); + + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) { + QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy); + } +} + +void tst_QHttp::constructing() +{ + //QHeader + { + QHttpRequestHeader header; + header.addValue("key1", "val1"); + header.addValue("key2", "val2"); + header.addValue("key1", "val3"); + QCOMPARE(header.values().size(), 3); + QCOMPARE(header.allValues("key1").size(), 2); + QVERIFY(header.hasKey("key2")); + QCOMPARE(header.keys().count(), 2); + + } + + { + QHttpResponseHeader header(200); + } +} + +void tst_QHttp::invalidRequests() +{ + QHttp http; + http.setHost("localhost"); // we will not actually connect + + QTest::ignoreMessage(QtWarningMsg, "QHttp: empty path requested is invalid -- using '/'"); + http.get(QString()); + + QTest::ignoreMessage(QtWarningMsg, "QHttp: empty path requested is invalid -- using '/'"); + http.head(QString()); + + QTest::ignoreMessage(QtWarningMsg, "QHttp: empty path requested is invalid -- using '/'"); + http.post(QString(), QByteArray()); + + QTest::ignoreMessage(QtWarningMsg, "QHttp: empty path requested is invalid -- using '/'"); + http.request(QHttpRequestHeader("PROPFIND", QString())); +} + +void tst_QHttp::get_data() +{ + QTest::addColumn("host"); + QTest::addColumn("port"); + QTest::addColumn("path"); + QTest::addColumn("success"); + QTest::addColumn("statusCode"); + QTest::addColumn("res"); + QTest::addColumn("useIODevice"); + + // ### move this into external testdata + QFile file( SRCDIR "rfc3252.txt" ); + QVERIFY( file.open( QIODevice::ReadOnly ) ); + QByteArray rfc3252 = file.readAll(); + file.close(); + + file.setFileName( SRCDIR "trolltech" ); + QVERIFY( file.open( QIODevice::ReadOnly ) ); + QByteArray trolltech = file.readAll(); + file.close(); + + // test the two get() modes in one routine + for ( int i=0; i<2; i++ ) { + QTest::newRow(QString("path_01_%1").arg(i).toLatin1()) << QtNetworkSettings::serverName() << 80u + << QString("/qtest/rfc3252.txt") << 1 << 200 << rfc3252 << (bool)(i==1); + QTest::newRow( QString("path_02_%1").arg(i).toLatin1() ) << QString("www.ietf.org") << 80u + << QString("/rfc/rfc3252.txt") << 1 << 200 << rfc3252 << (bool)(i==1); + + QTest::newRow( QString("uri_01_%1").arg(i).toLatin1() ) << QtNetworkSettings::serverName() << 80u + << QString("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt") << 1 << 200 << rfc3252 << (bool)(i==1); + QTest::newRow( QString("uri_02_%1").arg(i).toLatin1() ) << "www.ietf.org" << 80u + << QString("http://www.ietf.org/rfc/rfc3252.txt") << 1 << 200 << rfc3252 << (bool)(i==1); + + QTest::newRow( QString("fail_01_%1").arg(i).toLatin1() ) << QString("this-host-will-not-exist.") << 80u + << QString("/qtest/rfc3252.txt") << 0 << 0 << QByteArray() << (bool)(i==1); + + QTest::newRow( QString("failprot_01_%1").arg(i).toLatin1() ) << QtNetworkSettings::serverName() << 80u + << QString("/t") << 1 << 404 << QByteArray() << (bool)(i==1); + QTest::newRow( QString("failprot_02_%1").arg(i).toLatin1() ) << QtNetworkSettings::serverName() << 80u + << QString("qtest/rfc3252.txt") << 1 << 400 << QByteArray() << (bool)(i==1); + + // qt.nokia.com/doc uses transfer-encoding=chunked + /* qt.nokia.com/doc no longer seams to be using chuncked encodig. + QTest::newRow( QString("chunked_01_%1").arg(i).toLatin1() ) << QString("test.troll.no") << 80u + << QString("/") << 1 << 200 << trolltech << (bool)(i==1); + */ + QTest::newRow( QString("chunked_02_%1").arg(i).toLatin1() ) << QtNetworkSettings::serverName() << 80u + << QString("/qtest/cgi-bin/rfc.cgi") << 1 << 200 << rfc3252 << (bool)(i==1); + } +} + +void tst_QHttp::get() +{ + // for the overload that takes a QIODevice + QByteArray buf_ba; + QBuffer buf( &buf_ba ); + + QFETCH( QString, host ); + QFETCH( uint, port ); + QFETCH( QString, path ); + QFETCH( bool, useIODevice ); + + http = newHttp(); + QCOMPARE( http->currentId(), 0 ); + QCOMPARE( (int)http->state(), (int)QHttp::Unconnected ); + + addRequest( QHttpRequestHeader(), http->setHost( host, port ) ); + if ( useIODevice ) { + buf.open( QIODevice::WriteOnly ); + getId = http->get( path, &buf ); + } else { + getId = http->get( path ); + } + addRequest( QHttpRequestHeader(), getId ); + + QTestEventLoop::instance().enterLoop( 50 ); + + if ( QTestEventLoop::instance().timeout() ) + QFAIL( "Network operation timed out" ); + + ResMapIt res = resultMap.find( getId ); + QVERIFY( res != resultMap.end() ); + if ( res.value().success!=1 && host=="www.ietf.org" ) { + // The nightly tests fail from time to time. In order to make them more + // stable, add some debug output that might help locate the problem (I + // can't reproduce the problem in the non-nightly builds). + qDebug( "Error %d: %s", http->error(), http->errorString().toLatin1().constData() ); + } + QTEST( res.value().success, "success" ); + if ( res.value().success ) { + QTEST( res.value().resp.statusCode(), "statusCode" ); + + QFETCH( QByteArray, res ); + if ( res.count() > 0 ) { + if ( useIODevice ) { + QCOMPARE(buf_ba, res); + if ( bytesDoneRead != bytesDone_init ) + QVERIFY( (int)buf_ba.size() == bytesDoneRead ); + } else { + QCOMPARE(readyRead_ba, res); + if ( bytesDoneRead != bytesDone_init ) + QVERIFY( (int)readyRead_ba.size() == bytesDoneRead ); + } + } + QVERIFY( bytesTotalRead != bytesTotal_init ); + if ( bytesTotalRead > 0 ) + QVERIFY( bytesDoneRead == bytesTotalRead ); + } else { + QVERIFY( !res.value().resp.isValid() ); + } +} + +void tst_QHttp::head_data() +{ + QTest::addColumn("host"); + QTest::addColumn("port"); + QTest::addColumn("path"); + QTest::addColumn("success"); + QTest::addColumn("statusCode"); + QTest::addColumn("contentLength"); + + QTest::newRow( "path_01" ) << QtNetworkSettings::serverName() << 80u + << QString("/qtest/rfc3252.txt") << 1 << 200 << 25962u; + + QTest::newRow( "path_02" ) << QString("www.ietf.org") << 80u + << QString("/rfc/rfc3252.txt") << 1 << 200 << 25962u; + + QTest::newRow( "uri_01" ) << QtNetworkSettings::serverName() << 80u + << QString("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt") << 1 << 200 << 25962u; + + QTest::newRow( "uri_02" ) << QString("www.ietf.org") << 80u + << QString("http://www.ietf.org/rfc/rfc3252.txt") << 1 << 200 << 25962u; + + QTest::newRow( "fail_01" ) << QString("this-host-will-not-exist.") << 80u + << QString("/qtest/rfc3252.txt") << 0 << 0 << 0u; + + QTest::newRow( "failprot_01" ) << QtNetworkSettings::serverName() << 80u + << QString("/t") << 1 << 404 << 0u; + + QTest::newRow( "failprot_02" ) << QtNetworkSettings::serverName() << 80u + << QString("qtest/rfc3252.txt") << 1 << 400 << 0u; + + /* qt.nokia.com/doc no longer seams to be using chuncked encodig. + QTest::newRow( "chunked_01" ) << QString("qt.nokia.com/doc") << 80u + << QString("/index.html") << 1 << 200 << 0u; + */ + QTest::newRow( "chunked_02" ) << QtNetworkSettings::serverName() << 80u + << QString("/qtest/cgi-bin/rfc.cgi") << 1 << 200 << 0u; +} + +void tst_QHttp::head() +{ + QFETCH( QString, host ); + QFETCH( uint, port ); + QFETCH( QString, path ); + + http = newHttp(); + QCOMPARE( http->currentId(), 0 ); + QCOMPARE( (int)http->state(), (int)QHttp::Unconnected ); + + addRequest( QHttpRequestHeader(), http->setHost( host, port ) ); + headId = http->head( path ); + addRequest( QHttpRequestHeader(), headId ); + + QTestEventLoop::instance().enterLoop( 30 ); + if ( QTestEventLoop::instance().timeout() ) + QFAIL( "Network operation timed out" ); + + ResMapIt res = resultMap.find( headId ); + QVERIFY( res != resultMap.end() ); + if ( res.value().success!=1 && host=="www.ietf.org" ) { + // The nightly tests fail from time to time. In order to make them more + // stable, add some debug output that might help locate the problem (I + // can't reproduce the problem in the non-nightly builds). + qDebug( "Error %d: %s", http->error(), http->errorString().toLatin1().constData() ); + } + QTEST( res.value().success, "success" ); + if ( res.value().success ) { + QTEST( res.value().resp.statusCode(), "statusCode" ); + QTEST( res.value().resp.contentLength(), "contentLength" ); + + QCOMPARE( (uint)readyRead_ba.size(), 0u ); + QVERIFY( bytesTotalRead == bytesTotal_init ); + QVERIFY( bytesDoneRead == bytesDone_init ); + } else { + QVERIFY( !res.value().resp.isValid() ); + } +} + +void tst_QHttp::post_data() +{ + QTest::addColumn("source"); + QTest::addColumn("useIODevice"); + QTest::addColumn("useProxy"); + QTest::addColumn("host"); + QTest::addColumn("port"); + QTest::addColumn("ssl"); + QTest::addColumn("path"); + QTest::addColumn("result"); + + QByteArray md5sum; + md5sum = "d41d8cd98f00b204e9800998ecf8427e"; + QTest::newRow("empty-data") + << QString() << false << false + << QtNetworkSettings::serverName() << 80 << false << "/qtest/cgi-bin/md5sum.cgi" << md5sum; + QTest::newRow("empty-device") + << QString() << true << false + << QtNetworkSettings::serverName() << 80 << false << "/qtest/cgi-bin/md5sum.cgi" << md5sum; + QTest::newRow("proxy-empty-data") + << QString() << false << true + << QtNetworkSettings::serverName() << 80 << false << "/qtest/cgi-bin/md5sum.cgi" << md5sum; + + md5sum = "b3e32ac459b99d3f59318f3ac31e4bee"; + QTest::newRow("data") << "rfc3252.txt" << false << false + << QtNetworkSettings::serverName() << 80 << false << "/qtest/cgi-bin/md5sum.cgi" + << md5sum; + QTest::newRow("device") << "rfc3252.txt" << true << false + << QtNetworkSettings::serverName() << 80 << false << "/qtest/cgi-bin/md5sum.cgi" + << md5sum; + QTest::newRow("proxy-data") << "rfc3252.txt" << false << true + << QtNetworkSettings::serverName() << 80 << false << "/qtest/cgi-bin/md5sum.cgi" + << md5sum; + +#ifndef QT_NO_OPENSSL + md5sum = "d41d8cd98f00b204e9800998ecf8427e"; + QTest::newRow("empty-data-ssl") + << QString() << false << false + << QtNetworkSettings::serverName() << 443 << true << "/qtest/cgi-bin/md5sum.cgi" << md5sum; + QTest::newRow("empty-device-ssl") + << QString() << true << false + << QtNetworkSettings::serverName() << 443 << true << "/qtest/cgi-bin/md5sum.cgi" << md5sum; + QTest::newRow("proxy-empty-data-ssl") + << QString() << false << true + << QtNetworkSettings::serverName() << 443 << true << "/qtest/cgi-bin/md5sum.cgi" << md5sum; + md5sum = "b3e32ac459b99d3f59318f3ac31e4bee"; + QTest::newRow("data-ssl") << "rfc3252.txt" << false << false + << QtNetworkSettings::serverName() << 443 << true << "/qtest/cgi-bin/md5sum.cgi" + << md5sum; + QTest::newRow("device-ssl") << "rfc3252.txt" << true << false + << QtNetworkSettings::serverName() << 443 << true << "/qtest/cgi-bin/md5sum.cgi" + << md5sum; + QTest::newRow("proxy-data-ssl") << "rfc3252.txt" << false << true + << QtNetworkSettings::serverName() << 443 << true << "/qtest/cgi-bin/md5sum.cgi" + << md5sum; +#endif + + // the following test won't work. See task 185996 +/* + QTest::newRow("proxy-device") << "rfc3252.txt" << true << true + << QtNetworkSettings::serverName() << 80 << "/qtest/cgi-bin/md5sum.cgi" + << md5sum; +*/ +} + +void tst_QHttp::post() +{ + QFETCH(QString, source); + QFETCH(bool, useIODevice); + QFETCH(bool, useProxy); + QFETCH(QString, host); + QFETCH(int, port); + QFETCH(bool, ssl); + QFETCH(QString, path); + + http = newHttp(useProxy); +#ifndef QT_NO_OPENSSL + QObject::connect(http, SIGNAL(sslErrors(const QList &)), + http, SLOT(ignoreSslErrors())); +#endif + QCOMPARE(http->currentId(), 0); + QCOMPARE((int)http->state(), (int)QHttp::Unconnected); + if (useProxy) + addRequest(QHttpRequestHeader(), http->setProxy(QtNetworkSettings::serverName(), 3129)); + addRequest(QHttpRequestHeader(), http->setHost(host, (ssl ? QHttp::ConnectionModeHttps : QHttp::ConnectionModeHttp), port)); + + // add the POST request + QFile file(SRCDIR + source); + QBuffer emptyBuffer; + QIODevice *dev; + if (!source.isEmpty()) { + QVERIFY(file.open(QIODevice::ReadOnly)); + dev = &file; + } else { + emptyBuffer.open(QIODevice::ReadOnly); + dev = &emptyBuffer; + } + + if (useIODevice) + postId = http->post(path, dev); + else + postId = http->post(path, dev->readAll()); + addRequest(QHttpRequestHeader(), postId); + + // run request + connect(http, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + QTestEventLoop::instance().enterLoop( 30 ); + + if ( QTestEventLoop::instance().timeout() ) + QFAIL( "Network operation timed out" ); + + ResMapIt res = resultMap.find(postId); + QVERIFY(res != resultMap.end()); + QVERIFY(res.value().success); + QCOMPARE(res.value().resp.statusCode(), 200); + QTEST(readyRead_ba.trimmed(), "result"); +} + +void tst_QHttp::request_data() +{ + QTest::addColumn("source"); + QTest::addColumn("useIODevice"); + QTest::addColumn("useProxy"); + QTest::addColumn("host"); + QTest::addColumn("port"); + QTest::addColumn("method"); + QTest::addColumn("path"); + QTest::addColumn("result"); + + QFile source(SRCDIR "rfc3252.txt"); + if (!source.open(QIODevice::ReadOnly)) + return; + + QByteArray contents = source.readAll(); + QByteArray md5sum = QCryptographicHash::hash(contents, QCryptographicHash::Md5).toHex() + '\n'; + QByteArray emptyMd5sum = "d41d8cd98f00b204e9800998ecf8427e\n"; + + QTest::newRow("head") << QString() << false << false << QtNetworkSettings::serverName() << 80 + << "HEAD" << "/qtest/rfc3252.txt" + << QByteArray(); + QTest::newRow("get") << QString() << false << false << QtNetworkSettings::serverName() << 80 + << "GET" << "/qtest/rfc3252.txt" + << contents; + QTest::newRow("post-empty-data") << QString() << false << false + << QtNetworkSettings::serverName() << 80 << "POST" << "/qtest/cgi-bin/md5sum.cgi" + << emptyMd5sum; + QTest::newRow("post-empty-device") << QString() << true << false + << QtNetworkSettings::serverName() << 80 << "POST" << "/qtest/cgi-bin/md5sum.cgi" + << emptyMd5sum; + QTest::newRow("post-data") << "rfc3252.txt" << false << false + << QtNetworkSettings::serverName() << 80 << "POST" << "/qtest/cgi-bin/md5sum.cgi" + << md5sum; + QTest::newRow("post-device") << "rfc3252.txt" << true << false + << QtNetworkSettings::serverName() << 80 << "POST" << "/qtest/cgi-bin/md5sum.cgi" + << md5sum; + + QTest::newRow("proxy-head") << QString() << false << true << QtNetworkSettings::serverName() << 80 + << "HEAD" << "/qtest/rfc3252.txt" + << QByteArray(); + QTest::newRow("proxy-get") << QString() << false << true << QtNetworkSettings::serverName() << 80 + << "GET" << "/qtest/rfc3252.txt" + << contents; + QTest::newRow("proxy-post-empty-data") << QString() << false << true + << QtNetworkSettings::serverName() << 80 << "POST" << "/qtest/cgi-bin/md5sum.cgi" + << emptyMd5sum; + QTest::newRow("proxy-post-data") << "rfc3252.txt" << false << true + << QtNetworkSettings::serverName() << 80 << "POST" << "/qtest/cgi-bin/md5sum.cgi" + << md5sum; + // the following test won't work. See task 185996 +/* + QTest::newRow("proxy-post-device") << "rfc3252.txt" << true << true + << QtNetworkSettings::serverName() << 80 << "POST" << "/qtest/cgi-bin/md5sum.cgi" + << md5sum; +*/ +} + +void tst_QHttp::request() +{ + QFETCH(QString, source); + QFETCH(bool, useIODevice); + QFETCH(bool, useProxy); + QFETCH(QString, host); + QFETCH(int, port); + QFETCH(QString, method); + QFETCH(QString, path); + + http = newHttp(useProxy); + QCOMPARE(http->currentId(), 0); + QCOMPARE((int)http->state(), (int)QHttp::Unconnected); + if (useProxy) + addRequest(QHttpRequestHeader(), http->setProxy(QtNetworkSettings::serverName(), 3129)); + addRequest(QHttpRequestHeader(), http->setHost(host, port)); + + QFile file(SRCDIR + source); + QBuffer emptyBuffer; + QIODevice *dev; + if (!source.isEmpty()) { + QVERIFY(file.open(QIODevice::ReadOnly)); + dev = &file; + } else { + emptyBuffer.open(QIODevice::ReadOnly); + dev = &emptyBuffer; + } + + // prepare the request + QHttpRequestHeader request; + request.setRequest(method, path, 1,1); + request.addValue("Host", host); + int *theId; + + if (method == "POST") + theId = &postId; + else if (method == "GET") + theId = &getId; + else if (method == "HEAD") + theId = &headId; + else + QFAIL("You're lazy! Please implement your test!"); + + // now send the request + if (useIODevice) + *theId = http->request(request, dev); + else + *theId = http->request(request, dev->readAll()); + addRequest(QHttpRequestHeader(), *theId); + + // run request + connect(http, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + QTestEventLoop::instance().enterLoop( 30 ); + + if ( QTestEventLoop::instance().timeout() ) + QFAIL( "Network operation timed out" ); + + ResMapIt res = resultMap.find(*theId); + QVERIFY(res != resultMap.end()); + QVERIFY(res.value().success); + QCOMPARE(res.value().resp.statusCode(), 200); + QTEST(readyRead_ba, "result"); +} + +void tst_QHttp::authorization_data() +{ + QTest::addColumn("host"); + QTest::addColumn("path"); + QTest::addColumn("user"); + QTest::addColumn("pass"); + QTest::addColumn("result"); + + QTest::newRow("correct password") << QtNetworkSettings::serverName() + << QString::fromLatin1("/qtest/rfcs-auth/index.html") + << QString::fromLatin1("httptest") + << QString::fromLatin1("httptest") + << 200; + + QTest::newRow("no password") << QtNetworkSettings::serverName() + << QString::fromLatin1("/qtest/rfcs-auth/index.html") + << QString::fromLatin1("") + << QString::fromLatin1("") + << 401; + + QTest::newRow("wrong password") << QtNetworkSettings::serverName() + << QString::fromLatin1("/qtest/rfcs-auth/index.html") + << QString::fromLatin1("maliciu0s") + << QString::fromLatin1("h4X0r") + << 401; +} + +void tst_QHttp::authorization() +{ + QFETCH(QString, host); + QFETCH(QString, path); + QFETCH(QString, user); + QFETCH(QString, pass); + QFETCH(int, result); + + QEventLoop loop; + + QHttp http; + connect(&http, SIGNAL(done(bool)), &loop, SLOT(quit())); + + if (!user.isEmpty()) + http.setUser(user, pass); + http.setHost(host); + int id = http.get(path); + Q_UNUSED(id); + + QTimer::singleShot(5000, &loop, SLOT(quit())); + loop.exec(); + + QCOMPARE(http.lastResponse().statusCode(), result); +} + +void tst_QHttp::proxy_data() +{ + QTest::addColumn("proxyhost"); + QTest::addColumn("port"); + QTest::addColumn("host"); + QTest::addColumn("path"); + QTest::addColumn("proxyuser"); + QTest::addColumn("proxypass"); + + QTest::newRow("qt-test-server") << QtNetworkSettings::serverName() << 3128 + << QString::fromLatin1("qt.nokia.com") << QString::fromLatin1("/") + << QString::fromLatin1("") << QString::fromLatin1(""); + QTest::newRow("qt-test-server pct") << QtNetworkSettings::serverName() << 3128 + << QString::fromLatin1("qt.nokia.com") << QString::fromLatin1("/%64eveloper") + << QString::fromLatin1("") << QString::fromLatin1(""); + QTest::newRow("qt-test-server-basic") << QtNetworkSettings::serverName() << 3129 + << QString::fromLatin1("qt.nokia.com") << QString::fromLatin1("/") + << QString::fromLatin1("qsockstest") << QString::fromLatin1("password"); + +#if 0 + // NTLM requires sending the same request three times for it to work + // the tst_QHttp class is too strict to handle the byte counts sent by dataSendProgress + // So don't run this test: + QTest::newRow("qt-test-server-ntlm") << QtNetworkSettings::serverName() << 3130 + << QString::fromLatin1("qt.nokia.com") << QString::fromLatin1("/") + << QString::fromLatin1("qsockstest") << QString::fromLatin1("password"); +#endif +} + +void tst_QHttp::proxy() +{ + QFETCH(QString, proxyhost); + QFETCH(int, port); + QFETCH(QString, host); + QFETCH(QString, path); + QFETCH(QString, proxyuser); + QFETCH(QString, proxypass); + + http = newHttp(!proxyuser.isEmpty()); + + QCOMPARE(http->currentId(), 0); + QCOMPARE((int)http->state(), (int)QHttp::Unconnected); + + addRequest(QHttpRequestHeader(), http->setProxy(proxyhost, port, proxyuser, proxypass)); + addRequest(QHttpRequestHeader(), http->setHost(host)); + getId = http->get(path); + addRequest(QHttpRequestHeader(), getId); + + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) + QFAIL("Network operation timed out"); + + ResMapIt res = resultMap.find(getId); + QVERIFY(res != resultMap.end()); + QVERIFY(res.value().success); + QCOMPARE(res.value().resp.statusCode(), 200); +} + +void tst_QHttp::proxy2() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + readyRead_ba.clear(); + + QHttp http; + http.setProxy(QtNetworkSettings::serverName(), 3128); + http.setHost(QtNetworkSettings::serverName()); + http.get("/index.html"); + http.get("/index.html"); + + connect(&http, SIGNAL(requestFinished(int, bool)), + this, SLOT(proxy2_slot())); + QTestEventLoop::instance().enterLoop(30); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(readyRead_ba.count("Welcome to qt-test-server"), 2); + + readyRead_ba.clear(); +} + +void tst_QHttp::proxy2_slot() +{ + QHttp *http = static_cast(sender()); + readyRead_ba.append(http->readAll()); + if (!http->hasPendingRequests()) + QTestEventLoop::instance().exitLoop(); +} + +void tst_QHttp::proxy3() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + readyRead_ba.clear(); + + QTcpSocket socket; + socket.setProxy(QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3128)); + + QHttp http; + http.setSocket(&socket); + http.setHost(QtNetworkSettings::serverName()); + http.get("/index.html"); + http.get("/index.html"); + + connect(&http, SIGNAL(requestFinished(int, bool)), + this, SLOT(proxy2_slot())); + QTestEventLoop::instance().enterLoop(30); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(readyRead_ba.count("Welcome to qt-test-server"), 2); + + readyRead_ba.clear(); +} + +// test QHttp::currentId() and QHttp::currentRequest() +#define CURRENTREQUEST_TEST \ + { \ + ResMapIt res = resultMap.find( http->currentId() ); \ + QVERIFY( res != resultMap.end() ); \ + if ( http->currentId() == getId ) { \ + QCOMPARE( http->currentRequest().method(), QString("GET") ); \ + } else if ( http->currentId() == headId ) { \ + QCOMPARE( http->currentRequest().method(), QString("HEAD") ); \ + } else if ( http->currentId() == postId ) { \ + QCOMPARE( http->currentRequest().method(), QString("POST") ); \ + } else { \ + QVERIFY( headerAreEqual( http->currentRequest(), res.value().req ) ); \ + } \ + } + +void tst_QHttp::requestStarted( int id ) +{ +#if defined( DUMP_SIGNALS ) + qDebug( "%d:requestStarted( %d )", http->currentId(), id ); +#endif + // make sure that the requestStarted and requestFinished are nested correctly + QVERIFY( current_id == 0 ); + current_id = id; + + QVERIFY( !ids.isEmpty() ); + QVERIFY( ids.first() == id ); + if ( ids.count() > 1 ) { + QVERIFY( http->hasPendingRequests() ); + } else { + QVERIFY( !http->hasPendingRequests() ); + } + + QVERIFY( http->currentId() == id ); + QVERIFY( cur_state == http->state() ); + + + + + CURRENTREQUEST_TEST; + + QVERIFY( http->error() == QHttp::NoError ); +} + +void tst_QHttp::requestFinished( int id, bool error ) +{ +#if defined( DUMP_SIGNALS ) + qDebug( "%d:requestFinished( %d, %d ) -- errorString: '%s'", + http->currentId(), id, (int)error, http->errorString().toAscii().data() ); +#endif + // make sure that the requestStarted and requestFinished are nested correctly + QVERIFY( current_id == id ); + current_id = 0; + + QVERIFY( !ids.isEmpty() ); + QVERIFY( ids.first() == id ); + if ( ids.count() > 1 ) { + QVERIFY( http->hasPendingRequests() ); + } else { + QVERIFY( !http->hasPendingRequests() ); + } + + if ( error ) { + QVERIFY( http->error() != QHttp::NoError ); + ids.clear(); + } else { + QVERIFY( http->error() == QHttp::NoError ); + ids.pop_front(); + } + + QVERIFY( http->currentId() == id ); + QVERIFY( cur_state == http->state() ); + CURRENTREQUEST_TEST; + + ResMapIt res = resultMap.find( http->currentId() ); + QVERIFY( res != resultMap.end() ); + QVERIFY( res.value().success == -1 ); + if ( error ) + res.value().success = 0; + else + res.value().success = 1; +} + +void tst_QHttp::done( bool error ) +{ +#if defined( DUMP_SIGNALS ) + qDebug( "%d:done( %d )", http->currentId(), (int)error ); +#endif + QVERIFY( http->currentId() == 0 ); + QVERIFY( current_id == 0 ); + QVERIFY( ids.isEmpty() ); + QVERIFY( cur_state == http->state() ); + QVERIFY( !http->hasPendingRequests() ); + + QVERIFY( done_success == -1 ); + if ( error ) { + QVERIFY( http->error() != QHttp::NoError ); + done_success = 0; + } else { + QVERIFY( http->error() == QHttp::NoError ); + done_success = 1; + } + QTestEventLoop::instance().exitLoop(); +} + +void tst_QHttp::stateChanged( int state ) +{ +#if defined( DUMP_SIGNALS ) + qDebug( "%d: stateChanged( %d )", http->currentId(), state ); +#endif + QCOMPARE( http->currentId(), current_id ); + if ( ids.count() > 0 ) + CURRENTREQUEST_TEST; + + QVERIFY( state != cur_state ); + QVERIFY( state == http->state() ); + if ( state != QHttp::Unconnected && !connectionWithAuth ) { + // make sure that the states are always emitted in the right order (for + // this, we assume an ordering on the enum values, which they have at + // the moment) + // connections with authentication will possibly reconnect, so ignore them + QVERIFY( cur_state < state ); + } + cur_state = state; + + if (state == QHttp::Connecting) { + bytesTotalSend = bytesTotal_init; + bytesDoneSend = bytesDone_init; + bytesTotalRead = bytesTotal_init; + bytesDoneRead = bytesDone_init; + } +} + +void tst_QHttp::responseHeaderReceived( const QHttpResponseHeader &header ) +{ +#if defined( DUMP_SIGNALS ) + qDebug( "%d: responseHeaderReceived(\n---{\n%s}---)", http->currentId(), header.toString().toAscii().data() ); +#endif + QCOMPARE( http->currentId(), current_id ); + if ( ids.count() > 1 ) { + QVERIFY( http->hasPendingRequests() ); + } else { + QVERIFY( !http->hasPendingRequests() ); + } + CURRENTREQUEST_TEST; + + resultMap[ http->currentId() ].resp = header; +} + +void tst_QHttp::readyRead( const QHttpResponseHeader & ) +{ +#if defined( DUMP_SIGNALS ) + qDebug( "%d: readyRead()", http->currentId() ); +#endif + QCOMPARE( http->currentId(), current_id ); + if ( ids.count() > 1 ) { + QVERIFY( http->hasPendingRequests() ); + } else { + QVERIFY( !http->hasPendingRequests() ); + } + QVERIFY( cur_state == http->state() ); + CURRENTREQUEST_TEST; + + if ( QTest::currentTestFunction() != QLatin1String("bytesAvailable") ) { + int oldSize = readyRead_ba.size(); + quint64 bytesAvail = http->bytesAvailable(); + QByteArray ba = http->readAll(); + QVERIFY( (quint64) ba.size() == bytesAvail ); + readyRead_ba.resize( oldSize + ba.size() ); + memcpy( readyRead_ba.data()+oldSize, ba.data(), ba.size() ); + + if ( bytesTotalRead > 0 ) { + QVERIFY( (int)readyRead_ba.size() <= bytesTotalRead ); + } + QVERIFY( (int)readyRead_ba.size() == bytesDoneRead ); + } +} + +void tst_QHttp::dataSendProgress( int done, int total ) +{ +#if defined( DUMP_SIGNALS ) + qDebug( "%d: dataSendProgress( %d, %d )", http->currentId(), done, total ); +#endif + QCOMPARE( http->currentId(), current_id ); + if ( ids.count() > 1 ) { + QVERIFY( http->hasPendingRequests() ); + } else { + QVERIFY( !http->hasPendingRequests() ); + } + QVERIFY( cur_state == http->state() ); + CURRENTREQUEST_TEST; + + if ( bytesTotalSend == bytesTotal_init ) { + bytesTotalSend = total; + } else { + QCOMPARE( bytesTotalSend, total ); + } + + QVERIFY( bytesTotalSend != bytesTotal_init ); + QVERIFY( bytesDoneSend <= done ); + bytesDoneSend = done; + if ( bytesTotalSend > 0 ) { + QVERIFY( bytesDoneSend <= bytesTotalSend ); + } + + if ( QTest::currentTestFunction() == QLatin1String("abort") ) { + // ### it would be nice if we could specify in our testdata when to do + // the abort + if ( done >= total/100000 ) { + if ( ids.count() != 1 ) { + // do abort only once + int tmpId = ids.first(); + ids.clear(); + ids << tmpId; + http->abort(); + } + } + } +} + +void tst_QHttp::dataReadProgress( int done, int total ) +{ +#if defined( DUMP_SIGNALS ) + qDebug( "%d: dataReadProgress( %d, %d )", http->currentId(), done, total ); +#endif + QCOMPARE( http->currentId(), current_id ); + if ( ids.count() > 1 ) { + QVERIFY( http->hasPendingRequests() ); + } else { + QVERIFY( !http->hasPendingRequests() ); + } + QVERIFY( cur_state == http->state() ); + CURRENTREQUEST_TEST; + + if ( bytesTotalRead == bytesTotal_init ) + bytesTotalRead = total; + else { + QVERIFY( bytesTotalRead == total ); + } + + QVERIFY( bytesTotalRead != bytesTotal_init ); + QVERIFY( bytesDoneRead <= done ); + bytesDoneRead = done; + if ( bytesTotalRead > 0 ) { + QVERIFY( bytesDoneRead <= bytesTotalRead ); + } + + if ( QTest::currentTestFunction() == QLatin1String("abort") ) { + // ### it would be nice if we could specify in our testdata when to do + // the abort + if ( done >= total/100000 ) { + if ( ids.count() != 1 ) { + // do abort only once + int tmpId = ids.first(); + ids.clear(); + ids << tmpId; + http->abort(); + } + } + } +} + + +QHttp *tst_QHttp::newHttp(bool withAuth) +{ + QHttp *nHttp = new QHttp( 0 ); + connect( nHttp, SIGNAL(requestStarted(int)), + SLOT(requestStarted(int)) ); + connect( nHttp, SIGNAL(requestFinished(int,bool)), + SLOT(requestFinished(int,bool)) ); + connect( nHttp, SIGNAL(done(bool)), + SLOT(done(bool)) ); + connect( nHttp, SIGNAL(stateChanged(int)), + SLOT(stateChanged(int)) ); + connect( nHttp, SIGNAL(responseHeaderReceived(const QHttpResponseHeader&)), + SLOT(responseHeaderReceived(const QHttpResponseHeader&)) ); + connect( nHttp, SIGNAL(readyRead(const QHttpResponseHeader&)), + SLOT(readyRead(const QHttpResponseHeader&)) ); + connect( nHttp, SIGNAL(dataSendProgress(int,int)), + SLOT(dataSendProgress(int,int)) ); + connect( nHttp, SIGNAL(dataReadProgress(int,int)), + SLOT(dataReadProgress(int,int)) ); + + connectionWithAuth = withAuth; + return nHttp; +} + +void tst_QHttp::addRequest( QHttpRequestHeader header, int id ) +{ + ids << id; + RequestResult res; + res.req = header; + res.success = -1; + resultMap[ id ] = res; +} + +bool tst_QHttp::headerAreEqual( const QHttpHeader &h1, const QHttpHeader &h2 ) +{ + if ( !h1.isValid() ) + return !h2.isValid(); + if ( !h2.isValid() ) + return !h1.isValid(); + + return h1.toString() == h2.toString(); +} + + +void tst_QHttp::reconnect() +{ + reconnect_state_connect_count = 0; + + QHttp http; + + QObject::connect(&http, SIGNAL(stateChanged(int)), this, SLOT(reconnect_state(int))); + http.setHost("trolltech.com", 80); + http.get("/company/index.html"); + http.setHost("trolltech.com", 8080); + http.get("/company/index.html"); + + QTestEventLoop::instance().enterLoop(60); + if (QTestEventLoop::instance().timeout()) + QFAIL("Network operation timed out"); + + QCOMPARE(reconnect_state_connect_count, 1); + + QTestEventLoop::instance().enterLoop(60); + if (QTestEventLoop::instance().timeout()) + QFAIL("Network operation timed out"); + + QCOMPARE(reconnect_state_connect_count, 2); +} + +void tst_QHttp::reconnect_state(int state) +{ + if (state == QHttp::Connecting) { + ++reconnect_state_connect_count; + QTestEventLoop::instance().exitLoop(); + } +} + +void tst_QHttp::setSocket() +{ + QHttp *http = new QHttp; + QPointer replacementSocket = new QTcpSocket; + http->setSocket(replacementSocket); + QCoreApplication::processEvents(); + delete http; + QVERIFY(replacementSocket); + delete replacementSocket; +} + +class Server : public QTcpServer +{ + Q_OBJECT +public: + Server() + { + connect(this, SIGNAL(newConnection()), + this, SLOT(serveConnection())); + } + +private slots: + void serveConnection() + { + QTcpSocket *socket = nextPendingConnection(); + socket->write("HTTP/1.1 404 Not found\r\n" + "content-length: 4\r\n\r\nabcd"); + socket->disconnectFromHost(); + }; +}; + +void tst_QHttp::unexpectedRemoteClose() +{ + QFETCH_GLOBAL(int, proxyType); + if (proxyType == QNetworkProxy::Socks5Proxy) { + // This test doesn't make sense for SOCKS5 + return; + } + + Server server; + server.listen(); + QCoreApplication::instance()->processEvents(); + + QEventLoop loop; + QTimer::singleShot(3000, &loop, SLOT(quit())); + + QHttp http; + QObject::connect(&http, SIGNAL(done(bool)), &loop, SLOT(quit())); + QSignalSpy finishedSpy(&http, SIGNAL(requestFinished(int, bool))); + QSignalSpy doneSpy(&http, SIGNAL(done(bool))); + + http.setHost("localhost", server.serverPort()); + http.get("/"); + http.get("/"); + http.get("/"); + + loop.exec(); + + QCOMPARE(finishedSpy.count(), 4); + QVERIFY(!finishedSpy.at(1).at(1).toBool()); + QVERIFY(!finishedSpy.at(2).at(1).toBool()); + QVERIFY(!finishedSpy.at(3).at(1).toBool()); + QCOMPARE(doneSpy.count(), 1); + QVERIFY(!doneSpy.at(0).at(0).toBool()); +} + +void tst_QHttp::pctEncodedPath() +{ + QHttpRequestHeader header; + header.setRequest("GET", "/index.asp/a=%20&b=%20&c=%20"); + QCOMPARE(header.toString(), QString("GET /index.asp/a=%20&b=%20&c=%20 HTTP/1.1\r\n\r\n")); +} + +void tst_QHttp::caseInsensitiveKeys() +{ + QHttpResponseHeader header("HTTP/1.1 200 OK\r\nContent-Length: 213\r\nX-Been-There: True\r\nLocation: http://www.TrollTech.com/\r\n\r\n"); + QVERIFY(header.hasKey("Content-Length")); + QVERIFY(header.hasKey("X-Been-There")); + QVERIFY(header.hasKey("Location")); + QVERIFY(header.hasKey("content-length")); + QVERIFY(header.hasKey("x-been-there")); + QVERIFY(header.hasKey("location")); + QCOMPARE(header.value("Content-Length"), QString("213")); + QCOMPARE(header.value("X-Been-There"), QString("True")); + QCOMPARE(header.value("Location"), QString("http://www.TrollTech.com/")); + QCOMPARE(header.value("content-length"), QString("213")); + QCOMPARE(header.value("x-been-there"), QString("True")); + QCOMPARE(header.value("location"), QString("http://www.TrollTech.com/")); + QCOMPARE(header.allValues("location"), QStringList("http://www.TrollTech.com/")); + + header.addValue("Content-Length", "213"); + header.addValue("Content-Length", "214"); + header.addValue("Content-Length", "215"); + qDebug() << header.toString(); +} + +void tst_QHttp::proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth) +{ + proxyAuthCalled = true; + auth->setUser("qsockstest"); + auth->setPassword("password"); +} + +void tst_QHttp::postAuthNtlm() +{ + QSKIP("NTLM not working", SkipAll); + + QHostInfo info = QHostInfo::fromName(QHostInfo::localHostName()); + QByteArray postData("Hello World"); + QHttp http; + + http.setHost(QtNetworkSettings::serverName()); + http.setProxy(QtNetworkSettings::serverName(), 3130); + http.post("/", postData); + + proxyAuthCalled = false; + connect(&http, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + + QObject::connect(&http, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(3); + QObject::disconnect(&http, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop())); + + QVERIFY(proxyAuthCalled); + QVERIFY(!QTestEventLoop::instance().timeout()); +}; + +void tst_QHttp::proxyAndSsl() +{ +#ifdef QT_NO_OPENSSL + QSKIP("No OpenSSL support in this platform", SkipAll); +#else + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + QHttp http; + + http.setHost(QtNetworkSettings::serverName(), QHttp::ConnectionModeHttps); + http.setProxy(QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3129)); + http.get("/"); + + proxyAuthCalled = false; + connect(&http, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + connect(&http, SIGNAL(sslErrors(QList)), + &http, SLOT(ignoreSslErrors())); + + QObject::connect(&http, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(3); + QObject::disconnect(&http, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop())); + + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(proxyAuthCalled); + + QHttpResponseHeader header = http.lastResponse(); + QVERIFY(header.isValid()); + QVERIFY(header.statusCode() < 400); // Should be 200, but as long as it's not an error, we're happy +#endif +} + +void tst_QHttp::cachingProxyAndSsl() +{ +#ifdef QT_NO_OPENSSL + QSKIP("No OpenSSL support in this platform", SkipAll); +#else + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + QHttp http; + + http.setHost(QtNetworkSettings::serverName(), QHttp::ConnectionModeHttps); + http.setProxy(QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129)); + http.get("/"); + + proxyAuthCalled = false; + connect(&http, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + + QObject::connect(&http, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(3); + QObject::disconnect(&http, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop())); + + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(!proxyAuthCalled); // NOT called! QHttp should get a socket error + QVERIFY(http.state() != QHttp::Connected); + + QHttpResponseHeader header = http.lastResponse(); + QVERIFY(!header.isValid()); +#endif +} + +void tst_QHttp::emptyBodyInReply() +{ + // Note: if this test starts failing, please verify the date on the file + // returned by Apache on http://netiks.troll.no/ + // It is right now hard-coded to the date below + QHttp http; + http.setHost(QtNetworkSettings::serverName()); + + QHttpRequestHeader headers("GET", "/"); + headers.addValue("If-Modified-Since", "Sun, 16 Nov 2008 12:29:51 GMT"); + headers.addValue("Host", QtNetworkSettings::serverName()); + http.request(headers); + + QObject::connect(&http, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QObject::disconnect(&http, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop())); + + QVERIFY(!QTestEventLoop::instance().timeout()); + + // check the reply + if (http.lastResponse().statusCode() != 304) { + qWarning() << http.lastResponse().statusCode() << qPrintable(http.lastResponse().reasonPhrase()); + qWarning() << "Last-Modified:" << qPrintable(http.lastResponse().value("last-modified")); + QFAIL("Server replied with the wrong status code; see warning output"); + } +} + +void tst_QHttp::abortSender() +{ + QHttp *http = qobject_cast(sender()); + if (http) + http->abort(); +} + +void tst_QHttp::abortInReadyRead() +{ + QHttp http; + http.setHost(QtNetworkSettings::serverName()); + http.get("/qtest/bigfile"); + + qRegisterMetaType(); + QSignalSpy spy(&http, SIGNAL(readyRead(QHttpResponseHeader))); + + QObject::connect(&http, SIGNAL(readyRead(QHttpResponseHeader)), this, SLOT(abortSender())); + QObject::connect(&http, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QObject::disconnect(&http, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop())); + + QVERIFY2(!QTestEventLoop::instance().timeout(), "Network timeout"); + QVERIFY(http.state() != QHttp::Connected); + + QCOMPARE(spy.count(), 1); +} + +void tst_QHttp::abortInResponseHeaderReceived() +{ + QHttp http; + http.setHost(QtNetworkSettings::serverName()); + http.get("/qtest/bigfile"); + + qRegisterMetaType(); + QSignalSpy spy(&http, SIGNAL(readyRead(QHttpResponseHeader))); + + QObject::connect(&http, SIGNAL(responseHeaderReceived(QHttpResponseHeader)), this, SLOT(abortSender())); + QObject::connect(&http, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QObject::disconnect(&http, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop())); + + QVERIFY2(!QTestEventLoop::instance().timeout(), "Network timeout"); + QVERIFY(http.state() != QHttp::Connected); + + QCOMPARE(spy.count(), 0); +} + +void tst_QHttp::connectionClose() +{ + // This was added in response to bug 176822 + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + QHttp http; + ClosingServer server; + http.setHost("localhost", QHttp::ConnectionModeHttps, server.serverPort()); + http.get("/login/gateway/processLogin"); + + // another possibility: + //http.setHost("nexus.passport.com", QHttp::ConnectionModeHttps, 443); + //http.get("/rdr/pprdr.asp"); + + QObject::connect(&http, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(900); + QObject::disconnect(&http, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop())); + + QVERIFY(!QTestEventLoop::instance().timeout()); +} + +void tst_QHttp::nestedEventLoop_slot(int id) +{ + if (!ids.contains(id)) + return; + QEventLoop subloop; + + // 16 seconds: fluke times out in 15 seconds, which triggers a QTcpSocket error + QTimer::singleShot(16000, &subloop, SLOT(quit())); + subloop.exec(); + + QTestEventLoop::instance().exitLoop(); +} + +void tst_QHttp::nestedEventLoop() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + http = new QHttp; + http->setHost(QtNetworkSettings::serverName()); + int getId = http->get("/"); + + ids.clear(); + ids << getId; + + QSignalSpy spy(http, SIGNAL(requestStarted(int))); + QSignalSpy spy2(http, SIGNAL(done(bool))); + + connect(http, SIGNAL(requestFinished(int,bool)), SLOT(nestedEventLoop_slot(int))); + QTestEventLoop::instance().enterLoop(20); + + QVERIFY2(!QTestEventLoop::instance().timeout(), "Network timeout"); + + // Find out how many signals with the first argument equalling our id were found + int spyCount = 0; + for (int i = 0; i < spy.count(); ++i) + if (spy.at(i).at(0).toInt() == getId) + ++spyCount; + + // each signal spied should have been emitted only once + QCOMPARE(spyCount, 1); + QCOMPARE(spy2.count(), 1); +} + +QTEST_MAIN(tst_QHttp) +#include "tst_qhttp.moc" diff --git a/tests/auto/network/access/qhttp/webserver/cgi-bin/retrieve_testfile.cgi b/tests/auto/network/access/qhttp/webserver/cgi-bin/retrieve_testfile.cgi new file mode 100755 index 0000000000..7896c505ca --- /dev/null +++ b/tests/auto/network/access/qhttp/webserver/cgi-bin/retrieve_testfile.cgi @@ -0,0 +1,6 @@ +#!/bin/sh + +echo "Content-type: text/plain"; +echo +cat testfile +echo "no file retrieved" > testfile diff --git a/tests/auto/network/access/qhttp/webserver/cgi-bin/rfc.cgi b/tests/auto/network/access/qhttp/webserver/cgi-bin/rfc.cgi new file mode 100755 index 0000000000..c68688ea31 --- /dev/null +++ b/tests/auto/network/access/qhttp/webserver/cgi-bin/rfc.cgi @@ -0,0 +1,5 @@ +#!/bin/sh + +echo "Content-type: text/plain"; +echo +cat ../rfc3252 diff --git a/tests/auto/network/access/qhttp/webserver/cgi-bin/store_testfile.cgi b/tests/auto/network/access/qhttp/webserver/cgi-bin/store_testfile.cgi new file mode 100755 index 0000000000..e950f2af04 --- /dev/null +++ b/tests/auto/network/access/qhttp/webserver/cgi-bin/store_testfile.cgi @@ -0,0 +1,6 @@ +#!/bin/sh + +echo "Content-type: text/plain"; +echo +echo "file stored under 'testfile'" +cat > testfile diff --git a/tests/auto/network/access/qhttp/webserver/index.html b/tests/auto/network/access/qhttp/webserver/index.html new file mode 100644 index 0000000000..b80c61bf0a --- /dev/null +++ b/tests/auto/network/access/qhttp/webserver/index.html @@ -0,0 +1,899 @@ + + + + + + +Network Working Group H. Kennedy +Request for Comments: 3252 Mimezine +Category: Informational 1 April 2002 + + + Binary Lexical Octet Ad-hoc Transport + +Status of this Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2002). All Rights Reserved. + +Abstract + + This document defines a reformulation of IP and two transport layer + protocols (TCP and UDP) as XML applications. + +1. Introduction + +1.1. Overview + + This document describes the Binary Lexical Octet Ad-hoc Transport + (BLOAT): a reformulation of a widely-deployed network-layer protocol + (IP [RFC791]), and two associated transport layer protocols (TCP + [RFC793] and UDP [RFC768]) as XML [XML] applications. It also + describes methods for transporting BLOAT over Ethernet and IEEE 802 + networks as well as encapsulating BLOAT in IP for gatewaying BLOAT + across the public Internet. + +1.2. Motivation + + The wild popularity of XML as a basis for application-level protocols + such as the Blocks Extensible Exchange Protocol [RFC3080], the Simple + Object Access Protocol [SOAP], and Jabber [JABBER] prompted + investigation into the possibility of extending the use of XML in the + protocol stack. Using XML at both the transport and network layer in + addition to the application layer would provide for an amazing amount + of power and flexibility while removing dependencies on proprietary + and hard-to-understand binary protocols. This protocol unification + would also allow applications to use a single XML parser for all + aspects of their operation, eliminating developer time spent figuring + out the intricacies of each new protocol, and moving the hard work of + + + + +Kennedy Informational [Page 1] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + parsing to the XML toolset. The use of XML also mitigates concerns + over "network vs. host" byte ordering which is at the root of many + network application bugs. + +1.3. Relation to Existing Protocols + + The reformulations specified in this RFC follow as closely as + possible the spirit of the RFCs on which they are based, and so MAY + contain elements or attributes that would not be needed in a pure + reworking (e.g. length attributes, which are implicit in XML.) + + The layering of network and transport protocols are maintained in + this RFC despite the optimizations that could be made if the line + were somewhat blurred (i.e. merging TCP and IP into a single, larger + element in the DTD) in order to foster future use of this protocol as + a basis for reformulating other protocols (such as ICMP.) + + Other than the encoding, the behavioral aspects of each of the + existing protocols remain unchanged. Routing, address spaces, TCP + congestion control, etc. behave as specified in the extant standards. + Adapting to new standards and experimental algorithm heuristics for + improving performance will become much easier once the move to BLOAT + has been completed. + +1.4. Requirement Levels + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in BCP 14, RFC 2119 + [RFC2119]. + +2. IPoXML + + This protocol MUST be implemented to be compliant with this RFC. + IPoXML is the root protocol REQUIRED for effective use of TCPoXML + (section 3.) and higher-level application protocols. + + The DTD for this document type can be found in section 7.1. + + The routing of IPoXML can be easily implemented on hosts with an XML + parser, as the regular structure lends itself handily to parsing and + validation of the document/datagram and then processing the + destination address, TTL, and checksum before sending it on to its + next-hop. + + The reformulation of IPv4 was chosen over IPv6 [RFC2460] due to the + wider deployment of IPv4 and the fact that implementing IPv6 as XML + would have exceeded the 1500 byte Ethernet MTU. + + + +Kennedy Informational [Page 2] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + All BLOAT implementations MUST use - and specify - the UTF-8 encoding + of RFC 2279 [RFC2279]. All BLOAT document/datagrams MUST be well- + formed and include the XMLDecl. + +2.1. IP Description + + A number of items have changed (for the better) from the original IP + specification. Bit-masks, where present have been converted into + human-readable values. IP addresses are listed in their dotted- + decimal notation [RFC1123]. Length and checksum values are present + as decimal integers. + + To calculate the length and checksum fields of the IP element, a + canonicalized form of the element MUST be used. The canonical form + SHALL have no whitespace (including newline characters) between + elements and only one space character between attributes. There + SHALL NOT be a space following the last attribute in an element. + + An iterative method SHOULD be used to calculate checksums, as the + length field will vary based on the size of the checksum. + + The payload element bears special attention. Due to the character + set restrictions of XML, the payload of IP datagrams (which MAY + contain arbitrary data) MUST be encoded for transport. This RFC + REQUIRES the contents of the payload to be encoded in the base-64 + encoding of RFC 2045 [RFC2045], but removes the requirement that the + encoded output MUST be wrapped on 76-character lines. + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 3] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +2.2. Example Datagram + + The following is an example IPoXML datagram with an empty payload: + + + + +
+ + + + + + + + + + + + + + + +
+ + +
+ +3. TCPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.2. + +3.1. TCP Description + + A number of items have changed from the original TCP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + To calculate the length and checksum fields of the TCP element, a + canonicalized form of the element MUST be used as in section 2.1. + + An iterative method SHOULD be used to calculate checksums as in + section 2.1. + + The payload element MUST be encoded as in section 2.1. + + + +Kennedy Informational [Page 4] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + The TCP offset element was expanded to a maximum of 255 from 16 to + allow for the increased size of the header in XML. + + TCPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +3.2. Example Datagram + + The following is an example TCPoXML datagram with an empty payload: + + + + + + + + + + + + + + + + + + + + + + + + +4. UDPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.3. + +4.1. UDP Description + + A number of items have changed from the original UDP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + + + + + + +Kennedy Informational [Page 5] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + To calculate the length and checksum fields of the UDP element, a + canonicalized form of the element MUST be used as in section 2.1. An + iterative method SHOULD be used to calculate checksums as in section + 2.1. + + The payload element MUST be encoded as in section 2.1. + + UDPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +4.2. Example Datagram + + The following is an example UDPoXML datagram with an empty payload: + + + + + + + + + + + + + + +5. Network Transport + + This document provides for the transmission of BLOAT datagrams over + two common families of physical layer transport. Future RFCs will + address additional transports as routing vendors catch up to the + specification, and we begin to see BLOAT routed across the Internet + backbone. + +5.1. Ethernet + + BLOAT is encapsulated in Ethernet datagrams as in [RFC894] with the + exception that the type field of the Ethernet frame MUST contain the + value 0xBEEF. The first 5 octets of the Ethernet frame payload will + be 0x3c 3f 78 6d 6c (" + --> + + + + +Kennedy Informational [Page 7] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 9] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 10] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + +7.2. TCPoXML DTD + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 11] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 12] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + +7.3. UDPoXML DTD + + + + + + + + + + + + + + + +Kennedy Informational [Page 13] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +8. Security Considerations + + XML, as a subset of SGML, has the same security considerations as + specified in SGML Media Types [RFC1874]. Security considerations + that apply to IP, TCP and UDP also likely apply to BLOAT as it does + not attempt to correct for issues not related to message format. + +9. References + + [JABBER] Miller, J., "Jabber", draft-miller-jabber-00.txt, + February 2002. (Work in Progress) + + [RFC768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, + August 1980. + + [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791, + September 1981. + + [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC + 793, September 1981. + + [RFC894] Hornig, C., "Standard for the Transmission of IP + Datagrams over Ethernet Networks.", RFC 894, April 1984. + + [RFC1042] Postel, J. and J. Reynolds, "Standard for the + Transmission of IP Datagrams Over IEEE 802 Networks", STD + 43, RFC 1042, February 1988. + + [RFC1123] Braden, R., "Requirements for Internet Hosts - + Application and Support", RFC 1123, October 1989. + + [RFC1874] Levinson, E., "SGML Media Types", RFC 1874, December + 1995. + + [RFC2003] Perkins, C., "IP Encapsulation within IP", RFC 2003, + October 1996. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", RFC 2279, January 1998. + + + + + +Kennedy Informational [Page 14] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6 + (IPv6) Specification", RFC 2460, December 1998. + + [RFC3080] Rose, M., "The Blocks Extensible Exchange Protocol Core", + RFC 3080, March 2001. + + [SOAP] Box, D., Ehnebuske, D., Kakivaya, G., Layman, A., + Mendelsohn, N., Nielsen, H. F., Thatte, S. Winer, D., + "Simple Object Access Protocol (SOAP) 1.1" World Wide Web + Consortium Note, May 2000 http://www.w3.org/TR/SOAP/ + + [XML] Bray, T., Paoli, J., Sperberg-McQueen, C. M., "Extensible + Markup Language (XML)" World Wide Web Consortium + Recommendation REC- xml-19980210. + http://www.w3.org/TR/1998/REC-xml-19980210 + +10. Author's Address + + Hugh Kennedy + Mimezine + 1060 West Addison + Chicago, IL 60613 + USA + + EMail: kennedyh@engin.umich.edu + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 15] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +11. Full Copyright Statement + + Copyright (C) The Internet Society (2002). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 16] + diff --git a/tests/auto/network/access/qhttp/webserver/rfc3252 b/tests/auto/network/access/qhttp/webserver/rfc3252 new file mode 100644 index 0000000000..b80c61bf0a --- /dev/null +++ b/tests/auto/network/access/qhttp/webserver/rfc3252 @@ -0,0 +1,899 @@ + + + + + + +Network Working Group H. Kennedy +Request for Comments: 3252 Mimezine +Category: Informational 1 April 2002 + + + Binary Lexical Octet Ad-hoc Transport + +Status of this Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2002). All Rights Reserved. + +Abstract + + This document defines a reformulation of IP and two transport layer + protocols (TCP and UDP) as XML applications. + +1. Introduction + +1.1. Overview + + This document describes the Binary Lexical Octet Ad-hoc Transport + (BLOAT): a reformulation of a widely-deployed network-layer protocol + (IP [RFC791]), and two associated transport layer protocols (TCP + [RFC793] and UDP [RFC768]) as XML [XML] applications. It also + describes methods for transporting BLOAT over Ethernet and IEEE 802 + networks as well as encapsulating BLOAT in IP for gatewaying BLOAT + across the public Internet. + +1.2. Motivation + + The wild popularity of XML as a basis for application-level protocols + such as the Blocks Extensible Exchange Protocol [RFC3080], the Simple + Object Access Protocol [SOAP], and Jabber [JABBER] prompted + investigation into the possibility of extending the use of XML in the + protocol stack. Using XML at both the transport and network layer in + addition to the application layer would provide for an amazing amount + of power and flexibility while removing dependencies on proprietary + and hard-to-understand binary protocols. This protocol unification + would also allow applications to use a single XML parser for all + aspects of their operation, eliminating developer time spent figuring + out the intricacies of each new protocol, and moving the hard work of + + + + +Kennedy Informational [Page 1] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + parsing to the XML toolset. The use of XML also mitigates concerns + over "network vs. host" byte ordering which is at the root of many + network application bugs. + +1.3. Relation to Existing Protocols + + The reformulations specified in this RFC follow as closely as + possible the spirit of the RFCs on which they are based, and so MAY + contain elements or attributes that would not be needed in a pure + reworking (e.g. length attributes, which are implicit in XML.) + + The layering of network and transport protocols are maintained in + this RFC despite the optimizations that could be made if the line + were somewhat blurred (i.e. merging TCP and IP into a single, larger + element in the DTD) in order to foster future use of this protocol as + a basis for reformulating other protocols (such as ICMP.) + + Other than the encoding, the behavioral aspects of each of the + existing protocols remain unchanged. Routing, address spaces, TCP + congestion control, etc. behave as specified in the extant standards. + Adapting to new standards and experimental algorithm heuristics for + improving performance will become much easier once the move to BLOAT + has been completed. + +1.4. Requirement Levels + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in BCP 14, RFC 2119 + [RFC2119]. + +2. IPoXML + + This protocol MUST be implemented to be compliant with this RFC. + IPoXML is the root protocol REQUIRED for effective use of TCPoXML + (section 3.) and higher-level application protocols. + + The DTD for this document type can be found in section 7.1. + + The routing of IPoXML can be easily implemented on hosts with an XML + parser, as the regular structure lends itself handily to parsing and + validation of the document/datagram and then processing the + destination address, TTL, and checksum before sending it on to its + next-hop. + + The reformulation of IPv4 was chosen over IPv6 [RFC2460] due to the + wider deployment of IPv4 and the fact that implementing IPv6 as XML + would have exceeded the 1500 byte Ethernet MTU. + + + +Kennedy Informational [Page 2] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + All BLOAT implementations MUST use - and specify - the UTF-8 encoding + of RFC 2279 [RFC2279]. All BLOAT document/datagrams MUST be well- + formed and include the XMLDecl. + +2.1. IP Description + + A number of items have changed (for the better) from the original IP + specification. Bit-masks, where present have been converted into + human-readable values. IP addresses are listed in their dotted- + decimal notation [RFC1123]. Length and checksum values are present + as decimal integers. + + To calculate the length and checksum fields of the IP element, a + canonicalized form of the element MUST be used. The canonical form + SHALL have no whitespace (including newline characters) between + elements and only one space character between attributes. There + SHALL NOT be a space following the last attribute in an element. + + An iterative method SHOULD be used to calculate checksums, as the + length field will vary based on the size of the checksum. + + The payload element bears special attention. Due to the character + set restrictions of XML, the payload of IP datagrams (which MAY + contain arbitrary data) MUST be encoded for transport. This RFC + REQUIRES the contents of the payload to be encoded in the base-64 + encoding of RFC 2045 [RFC2045], but removes the requirement that the + encoded output MUST be wrapped on 76-character lines. + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 3] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +2.2. Example Datagram + + The following is an example IPoXML datagram with an empty payload: + + + + +
+ + + + + + + + + + + + + + + +
+ + +
+ +3. TCPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.2. + +3.1. TCP Description + + A number of items have changed from the original TCP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + To calculate the length and checksum fields of the TCP element, a + canonicalized form of the element MUST be used as in section 2.1. + + An iterative method SHOULD be used to calculate checksums as in + section 2.1. + + The payload element MUST be encoded as in section 2.1. + + + +Kennedy Informational [Page 4] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + The TCP offset element was expanded to a maximum of 255 from 16 to + allow for the increased size of the header in XML. + + TCPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +3.2. Example Datagram + + The following is an example TCPoXML datagram with an empty payload: + + + + + + + + + + + + + + + + + + + + + + + + +4. UDPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.3. + +4.1. UDP Description + + A number of items have changed from the original UDP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + + + + + + +Kennedy Informational [Page 5] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + To calculate the length and checksum fields of the UDP element, a + canonicalized form of the element MUST be used as in section 2.1. An + iterative method SHOULD be used to calculate checksums as in section + 2.1. + + The payload element MUST be encoded as in section 2.1. + + UDPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +4.2. Example Datagram + + The following is an example UDPoXML datagram with an empty payload: + + + + + + + + + + + + + + +5. Network Transport + + This document provides for the transmission of BLOAT datagrams over + two common families of physical layer transport. Future RFCs will + address additional transports as routing vendors catch up to the + specification, and we begin to see BLOAT routed across the Internet + backbone. + +5.1. Ethernet + + BLOAT is encapsulated in Ethernet datagrams as in [RFC894] with the + exception that the type field of the Ethernet frame MUST contain the + value 0xBEEF. The first 5 octets of the Ethernet frame payload will + be 0x3c 3f 78 6d 6c (" + --> + + + + +Kennedy Informational [Page 7] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 9] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 10] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + +7.2. TCPoXML DTD + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 11] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 12] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + +7.3. UDPoXML DTD + + + + + + + + + + + + + + + +Kennedy Informational [Page 13] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +8. Security Considerations + + XML, as a subset of SGML, has the same security considerations as + specified in SGML Media Types [RFC1874]. Security considerations + that apply to IP, TCP and UDP also likely apply to BLOAT as it does + not attempt to correct for issues not related to message format. + +9. References + + [JABBER] Miller, J., "Jabber", draft-miller-jabber-00.txt, + February 2002. (Work in Progress) + + [RFC768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, + August 1980. + + [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791, + September 1981. + + [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC + 793, September 1981. + + [RFC894] Hornig, C., "Standard for the Transmission of IP + Datagrams over Ethernet Networks.", RFC 894, April 1984. + + [RFC1042] Postel, J. and J. Reynolds, "Standard for the + Transmission of IP Datagrams Over IEEE 802 Networks", STD + 43, RFC 1042, February 1988. + + [RFC1123] Braden, R., "Requirements for Internet Hosts - + Application and Support", RFC 1123, October 1989. + + [RFC1874] Levinson, E., "SGML Media Types", RFC 1874, December + 1995. + + [RFC2003] Perkins, C., "IP Encapsulation within IP", RFC 2003, + October 1996. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", RFC 2279, January 1998. + + + + + +Kennedy Informational [Page 14] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6 + (IPv6) Specification", RFC 2460, December 1998. + + [RFC3080] Rose, M., "The Blocks Extensible Exchange Protocol Core", + RFC 3080, March 2001. + + [SOAP] Box, D., Ehnebuske, D., Kakivaya, G., Layman, A., + Mendelsohn, N., Nielsen, H. F., Thatte, S. Winer, D., + "Simple Object Access Protocol (SOAP) 1.1" World Wide Web + Consortium Note, May 2000 http://www.w3.org/TR/SOAP/ + + [XML] Bray, T., Paoli, J., Sperberg-McQueen, C. M., "Extensible + Markup Language (XML)" World Wide Web Consortium + Recommendation REC- xml-19980210. + http://www.w3.org/TR/1998/REC-xml-19980210 + +10. Author's Address + + Hugh Kennedy + Mimezine + 1060 West Addison + Chicago, IL 60613 + USA + + EMail: kennedyh@engin.umich.edu + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 15] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +11. Full Copyright Statement + + Copyright (C) The Internet Society (2002). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 16] + diff --git a/tests/auto/network/access/qhttp/webserver/rfc3252.txt b/tests/auto/network/access/qhttp/webserver/rfc3252.txt new file mode 100644 index 0000000000..b80c61bf0a --- /dev/null +++ b/tests/auto/network/access/qhttp/webserver/rfc3252.txt @@ -0,0 +1,899 @@ + + + + + + +Network Working Group H. Kennedy +Request for Comments: 3252 Mimezine +Category: Informational 1 April 2002 + + + Binary Lexical Octet Ad-hoc Transport + +Status of this Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2002). All Rights Reserved. + +Abstract + + This document defines a reformulation of IP and two transport layer + protocols (TCP and UDP) as XML applications. + +1. Introduction + +1.1. Overview + + This document describes the Binary Lexical Octet Ad-hoc Transport + (BLOAT): a reformulation of a widely-deployed network-layer protocol + (IP [RFC791]), and two associated transport layer protocols (TCP + [RFC793] and UDP [RFC768]) as XML [XML] applications. It also + describes methods for transporting BLOAT over Ethernet and IEEE 802 + networks as well as encapsulating BLOAT in IP for gatewaying BLOAT + across the public Internet. + +1.2. Motivation + + The wild popularity of XML as a basis for application-level protocols + such as the Blocks Extensible Exchange Protocol [RFC3080], the Simple + Object Access Protocol [SOAP], and Jabber [JABBER] prompted + investigation into the possibility of extending the use of XML in the + protocol stack. Using XML at both the transport and network layer in + addition to the application layer would provide for an amazing amount + of power and flexibility while removing dependencies on proprietary + and hard-to-understand binary protocols. This protocol unification + would also allow applications to use a single XML parser for all + aspects of their operation, eliminating developer time spent figuring + out the intricacies of each new protocol, and moving the hard work of + + + + +Kennedy Informational [Page 1] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + parsing to the XML toolset. The use of XML also mitigates concerns + over "network vs. host" byte ordering which is at the root of many + network application bugs. + +1.3. Relation to Existing Protocols + + The reformulations specified in this RFC follow as closely as + possible the spirit of the RFCs on which they are based, and so MAY + contain elements or attributes that would not be needed in a pure + reworking (e.g. length attributes, which are implicit in XML.) + + The layering of network and transport protocols are maintained in + this RFC despite the optimizations that could be made if the line + were somewhat blurred (i.e. merging TCP and IP into a single, larger + element in the DTD) in order to foster future use of this protocol as + a basis for reformulating other protocols (such as ICMP.) + + Other than the encoding, the behavioral aspects of each of the + existing protocols remain unchanged. Routing, address spaces, TCP + congestion control, etc. behave as specified in the extant standards. + Adapting to new standards and experimental algorithm heuristics for + improving performance will become much easier once the move to BLOAT + has been completed. + +1.4. Requirement Levels + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in BCP 14, RFC 2119 + [RFC2119]. + +2. IPoXML + + This protocol MUST be implemented to be compliant with this RFC. + IPoXML is the root protocol REQUIRED for effective use of TCPoXML + (section 3.) and higher-level application protocols. + + The DTD for this document type can be found in section 7.1. + + The routing of IPoXML can be easily implemented on hosts with an XML + parser, as the regular structure lends itself handily to parsing and + validation of the document/datagram and then processing the + destination address, TTL, and checksum before sending it on to its + next-hop. + + The reformulation of IPv4 was chosen over IPv6 [RFC2460] due to the + wider deployment of IPv4 and the fact that implementing IPv6 as XML + would have exceeded the 1500 byte Ethernet MTU. + + + +Kennedy Informational [Page 2] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + All BLOAT implementations MUST use - and specify - the UTF-8 encoding + of RFC 2279 [RFC2279]. All BLOAT document/datagrams MUST be well- + formed and include the XMLDecl. + +2.1. IP Description + + A number of items have changed (for the better) from the original IP + specification. Bit-masks, where present have been converted into + human-readable values. IP addresses are listed in their dotted- + decimal notation [RFC1123]. Length and checksum values are present + as decimal integers. + + To calculate the length and checksum fields of the IP element, a + canonicalized form of the element MUST be used. The canonical form + SHALL have no whitespace (including newline characters) between + elements and only one space character between attributes. There + SHALL NOT be a space following the last attribute in an element. + + An iterative method SHOULD be used to calculate checksums, as the + length field will vary based on the size of the checksum. + + The payload element bears special attention. Due to the character + set restrictions of XML, the payload of IP datagrams (which MAY + contain arbitrary data) MUST be encoded for transport. This RFC + REQUIRES the contents of the payload to be encoded in the base-64 + encoding of RFC 2045 [RFC2045], but removes the requirement that the + encoded output MUST be wrapped on 76-character lines. + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 3] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +2.2. Example Datagram + + The following is an example IPoXML datagram with an empty payload: + + + + +
+ + + + + + + + + + + + + + + +
+ + +
+ +3. TCPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.2. + +3.1. TCP Description + + A number of items have changed from the original TCP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + To calculate the length and checksum fields of the TCP element, a + canonicalized form of the element MUST be used as in section 2.1. + + An iterative method SHOULD be used to calculate checksums as in + section 2.1. + + The payload element MUST be encoded as in section 2.1. + + + +Kennedy Informational [Page 4] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + The TCP offset element was expanded to a maximum of 255 from 16 to + allow for the increased size of the header in XML. + + TCPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +3.2. Example Datagram + + The following is an example TCPoXML datagram with an empty payload: + + + + + + + + + + + + + + + + + + + + + + + + +4. UDPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.3. + +4.1. UDP Description + + A number of items have changed from the original UDP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + + + + + + +Kennedy Informational [Page 5] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + To calculate the length and checksum fields of the UDP element, a + canonicalized form of the element MUST be used as in section 2.1. An + iterative method SHOULD be used to calculate checksums as in section + 2.1. + + The payload element MUST be encoded as in section 2.1. + + UDPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +4.2. Example Datagram + + The following is an example UDPoXML datagram with an empty payload: + + + + + + + + + + + + + + +5. Network Transport + + This document provides for the transmission of BLOAT datagrams over + two common families of physical layer transport. Future RFCs will + address additional transports as routing vendors catch up to the + specification, and we begin to see BLOAT routed across the Internet + backbone. + +5.1. Ethernet + + BLOAT is encapsulated in Ethernet datagrams as in [RFC894] with the + exception that the type field of the Ethernet frame MUST contain the + value 0xBEEF. The first 5 octets of the Ethernet frame payload will + be 0x3c 3f 78 6d 6c (" + --> + + + + +Kennedy Informational [Page 7] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 9] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 10] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + +7.2. TCPoXML DTD + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 11] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 12] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + +7.3. UDPoXML DTD + + + + + + + + + + + + + + + +Kennedy Informational [Page 13] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +8. Security Considerations + + XML, as a subset of SGML, has the same security considerations as + specified in SGML Media Types [RFC1874]. Security considerations + that apply to IP, TCP and UDP also likely apply to BLOAT as it does + not attempt to correct for issues not related to message format. + +9. References + + [JABBER] Miller, J., "Jabber", draft-miller-jabber-00.txt, + February 2002. (Work in Progress) + + [RFC768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, + August 1980. + + [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791, + September 1981. + + [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC + 793, September 1981. + + [RFC894] Hornig, C., "Standard for the Transmission of IP + Datagrams over Ethernet Networks.", RFC 894, April 1984. + + [RFC1042] Postel, J. and J. Reynolds, "Standard for the + Transmission of IP Datagrams Over IEEE 802 Networks", STD + 43, RFC 1042, February 1988. + + [RFC1123] Braden, R., "Requirements for Internet Hosts - + Application and Support", RFC 1123, October 1989. + + [RFC1874] Levinson, E., "SGML Media Types", RFC 1874, December + 1995. + + [RFC2003] Perkins, C., "IP Encapsulation within IP", RFC 2003, + October 1996. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", RFC 2279, January 1998. + + + + + +Kennedy Informational [Page 14] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6 + (IPv6) Specification", RFC 2460, December 1998. + + [RFC3080] Rose, M., "The Blocks Extensible Exchange Protocol Core", + RFC 3080, March 2001. + + [SOAP] Box, D., Ehnebuske, D., Kakivaya, G., Layman, A., + Mendelsohn, N., Nielsen, H. F., Thatte, S. Winer, D., + "Simple Object Access Protocol (SOAP) 1.1" World Wide Web + Consortium Note, May 2000 http://www.w3.org/TR/SOAP/ + + [XML] Bray, T., Paoli, J., Sperberg-McQueen, C. M., "Extensible + Markup Language (XML)" World Wide Web Consortium + Recommendation REC- xml-19980210. + http://www.w3.org/TR/1998/REC-xml-19980210 + +10. Author's Address + + Hugh Kennedy + Mimezine + 1060 West Addison + Chicago, IL 60613 + USA + + EMail: kennedyh@engin.umich.edu + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 15] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +11. Full Copyright Statement + + Copyright (C) The Internet Society (2002). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 16] + diff --git a/tests/auto/network/access/qhttpnetworkconnection/.gitignore b/tests/auto/network/access/qhttpnetworkconnection/.gitignore new file mode 100644 index 0000000000..4fe208868b --- /dev/null +++ b/tests/auto/network/access/qhttpnetworkconnection/.gitignore @@ -0,0 +1 @@ +tst_qhttpnetworkconnection diff --git a/tests/auto/network/access/qhttpnetworkconnection/qhttpnetworkconnection.pro b/tests/auto/network/access/qhttpnetworkconnection/qhttpnetworkconnection.pro new file mode 100644 index 0000000000..1ce5d8a092 --- /dev/null +++ b/tests/auto/network/access/qhttpnetworkconnection/qhttpnetworkconnection.pro @@ -0,0 +1,13 @@ +load(qttest_p4) +SOURCES += tst_qhttpnetworkconnection.cpp +INCLUDEPATH += $$QT_SOURCE_TREE/src/3rdparty/zlib +requires(contains(QT_CONFIG,private_tests)) + +QT = core-private network-private + +symbian: TARGET.CAPABILITY = NetworkServices +symbian: { + INCLUDEPATH += $$MW_LAYER_SYSTEMINCLUDE +} + +CONFIG+=insignificant_test # QTBUG-20981, crashes sometimes diff --git a/tests/auto/network/access/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp b/tests/auto/network/access/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp new file mode 100644 index 0000000000..2d30f6bca1 --- /dev/null +++ b/tests/auto/network/access/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp @@ -0,0 +1,1131 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include "private/qhttpnetworkconnection_p.h" +#include "private/qnoncontiguousbytedevice_p.h" +#include + +#include "../../../network-settings.h" + +class tst_QHttpNetworkConnection: public QObject +{ + Q_OBJECT + +public: + tst_QHttpNetworkConnection(); + +public Q_SLOTS: + void finishedReply(); + void finishedWithError(QNetworkReply::NetworkError errorCode, const QString &detail); + void challenge401(const QHttpNetworkRequest &request, QAuthenticator *authenticator); +#ifndef QT_NO_OPENSSL + void sslErrors(const QList &errors); +#endif +private: + bool finishedCalled; + bool finishedWithErrorCalled; + QNetworkReply::NetworkError netErrorCode; + +private Q_SLOTS: + void init(); + void cleanup(); + void initTestCase(); + void cleanupTestCase(); + + void options_data(); + void options(); + void get_data(); + void get(); + void head_data(); + void head(); + void post_data(); + void post(); + void put_data(); + void put(); + void _delete_data(); + void _delete(); + void trace_data(); + void trace(); + void _connect_data(); + void _connect(); +#ifndef QT_NO_COMPRESS + void compression_data(); + void compression(); +#endif +#ifndef QT_NO_OPENSSL + void ignoresslerror_data(); + void ignoresslerror(); +#endif +#ifdef QT_NO_OPENSSL + void nossl_data(); + void nossl(); +#endif + void get401_data(); + void get401(); + + void getMultiple_data(); + void getMultiple(); + void getMultipleWithPipeliningAndMultiplePriorities(); + void getMultipleWithPriorities(); + + void getEmptyWithPipelining(); + + void getAndEverythingShouldBePipelined(); + + void getAndThenDeleteObject(); + void getAndThenDeleteObject_data(); +}; + +tst_QHttpNetworkConnection::tst_QHttpNetworkConnection() +{ + Q_SET_DEFAULT_IAP +} + +void tst_QHttpNetworkConnection::initTestCase() +{ +} + +void tst_QHttpNetworkConnection::cleanupTestCase() +{ +} + +void tst_QHttpNetworkConnection::init() +{ +} + +void tst_QHttpNetworkConnection::cleanup() +{ +} + +void tst_QHttpNetworkConnection::options_data() +{ + // not tested yet +} + +void tst_QHttpNetworkConnection::options() +{ + QEXPECT_FAIL("", "not tested yet", Continue); + QVERIFY(false); +} + +void tst_QHttpNetworkConnection::head_data() +{ + QTest::addColumn("protocol"); + QTest::addColumn("host"); + QTest::addColumn("path"); + QTest::addColumn("port"); + QTest::addColumn("encrypt"); + QTest::addColumn("statusCode"); + QTest::addColumn("statusString"); + QTest::addColumn("contentLength"); + + QTest::newRow("success-internal") << "http://" << QtNetworkSettings::serverName() << "/qtest/rfc3252.txt" << ushort(80) << false << 200 << "OK" << 25962; + QTest::newRow("success-external") << "http://" << "www.ietf.org" << "/rfc/rfc3252.txt" << ushort(80) << false << 200 << "OK" << 25962; + + QTest::newRow("failure-path") << "http://" << QtNetworkSettings::serverName() << "/t" << ushort(80) << false << 404 << "Not Found" << -1; + QTest::newRow("failure-protocol") << "" << QtNetworkSettings::serverName() << "/qtest/rfc3252.txt" << ushort(80) << false << 400 << "Bad Request" << -1; +} + +void tst_QHttpNetworkConnection::head() +{ + QFETCH(QString, protocol); + QFETCH(QString, host); + QFETCH(QString, path); + QFETCH(ushort, port); + QFETCH(bool, encrypt); + QFETCH(int, statusCode); + QFETCH(QString, statusString); + QFETCH(int, contentLength); + + QHttpNetworkConnection connection(host, port, encrypt); + QCOMPARE(connection.port(), port); + QCOMPARE(connection.hostName(), host); + QCOMPARE(connection.isSsl(), encrypt); + + QHttpNetworkRequest request(protocol + host + path, QHttpNetworkRequest::Head); + QHttpNetworkReply *reply = connection.sendRequest(request); + + QTime stopWatch; + stopWatch.start(); + do { + QCoreApplication::instance()->processEvents(); + if (stopWatch.elapsed() >= 30000) + break; + } while (!reply->isFinished()); + + QCOMPARE(reply->statusCode(), statusCode); + QCOMPARE(reply->reasonPhrase(), statusString); + // only check it if it is set and expected + if (reply->contentLength() != -1 && contentLength != -1) + QCOMPARE(reply->contentLength(), qint64(contentLength)); + + QVERIFY(reply->isFinished()); + + delete reply; +} + +void tst_QHttpNetworkConnection::get_data() +{ + QTest::addColumn("protocol"); + QTest::addColumn("host"); + QTest::addColumn("path"); + QTest::addColumn("port"); + QTest::addColumn("encrypt"); + QTest::addColumn("statusCode"); + QTest::addColumn("statusString"); + QTest::addColumn("contentLength"); + QTest::addColumn("downloadSize"); + + QTest::newRow("success-internal") << "http://" << QtNetworkSettings::serverName() << "/qtest/rfc3252.txt" << ushort(80) << false << 200 << "OK" << 25962 << 25962; + QTest::newRow("success-external") << "http://" << "www.ietf.org" << "/rfc/rfc3252.txt" << ushort(80) << false << 200 << "OK" << 25962 << 25962; + + QTest::newRow("failure-path") << "http://" << QtNetworkSettings::serverName() << "/t" << ushort(80) << false << 404 << "Not Found" << -1 << -1; + QTest::newRow("failure-protocol") << "" << QtNetworkSettings::serverName() << "/qtest/rfc3252.txt" << ushort(80) << false << 400 << "Bad Request" << -1 << -1; +} + +void tst_QHttpNetworkConnection::get() +{ + QFETCH(QString, protocol); + QFETCH(QString, host); + QFETCH(QString, path); + QFETCH(ushort, port); + QFETCH(bool, encrypt); + QFETCH(int, statusCode); + QFETCH(QString, statusString); + QFETCH(int, contentLength); + QFETCH(int, downloadSize); + + QHttpNetworkConnection connection(host, port, encrypt); + QCOMPARE(connection.port(), port); + QCOMPARE(connection.hostName(), host); + QCOMPARE(connection.isSsl(), encrypt); + + QHttpNetworkRequest request(protocol + host + path); + QHttpNetworkReply *reply = connection.sendRequest(request); + + QTime stopWatch; + stopWatch.start(); + forever { + QCoreApplication::instance()->processEvents(); + if (reply->bytesAvailable()) + break; + if (stopWatch.elapsed() >= 30000) + break; + } + + QCOMPARE(reply->statusCode(), statusCode); + QCOMPARE(reply->reasonPhrase(), statusString); + // only check it if it is set and expected + if (reply->contentLength() != -1 && contentLength != -1) + QCOMPARE(reply->contentLength(), qint64(contentLength)); + + stopWatch.start(); + QByteArray ba; + do { + QCoreApplication::instance()->processEvents(); + while (reply->bytesAvailable()) + ba += reply->readAny(); + if (stopWatch.elapsed() >= 30000) + break; + } while (!reply->isFinished()); + + QVERIFY(reply->isFinished()); + //do not require server generated error pages to be a fixed size + if (downloadSize != -1) + QCOMPARE(ba.size(), downloadSize); + //but check against content length if it was sent + if (reply->contentLength() != -1) + QCOMPARE(ba.size(), (int)reply->contentLength()); + + delete reply; +} + +void tst_QHttpNetworkConnection::finishedReply() +{ + finishedCalled = true; +} + +void tst_QHttpNetworkConnection::finishedWithError(QNetworkReply::NetworkError errorCode, const QString &detail) +{ + Q_UNUSED(detail) + finishedWithErrorCalled = true; + netErrorCode = errorCode; +} + +void tst_QHttpNetworkConnection::put_data() +{ + + QTest::addColumn("protocol"); + QTest::addColumn("host"); + QTest::addColumn("path"); + QTest::addColumn("port"); + QTest::addColumn("encrypt"); + QTest::addColumn("data"); + QTest::addColumn("succeed"); + + QTest::newRow("success-internal") << "http://" << QtNetworkSettings::serverName() << "/dav/file1.txt" << ushort(80) << false << "Hello World\nEnd of file\n"<setParent(this); + request.setUploadByteDevice(bd); + + finishedCalled = false; + finishedWithErrorCalled = false; + + QHttpNetworkReply *reply = connection.sendRequest(request); + connect(reply, SIGNAL(finished()), SLOT(finishedReply())); + connect(reply, SIGNAL(finishedWithError(QNetworkReply::NetworkError, const QString &)), + SLOT(finishedWithError(QNetworkReply::NetworkError, const QString &))); + + QTime stopWatch; + stopWatch.start(); + do { + QCoreApplication::instance()->processEvents(); + if (stopWatch.elapsed() >= 30000) + break; + } while (!reply->isFinished() && !finishedCalled && !finishedWithErrorCalled); + + if (reply->isFinished()) { + QByteArray ba; + while (reply->bytesAvailable()) + ba += reply->readAny(); + } else if(finishedWithErrorCalled) { + if(!succeed) { + delete reply; + return; + } else { + QFAIL("Error in PUT"); + } + } else { + QFAIL("PUT timed out"); + } + + int status = reply->statusCode(); + if (status != 200 && status != 201 && status != 204) { + if (succeed) { + qDebug()<<"PUT failed, Status Code:" <("protocol"); + QTest::addColumn("host"); + QTest::addColumn("path"); + QTest::addColumn("port"); + QTest::addColumn("encrypt"); + QTest::addColumn("data"); + QTest::addColumn("statusCode"); + QTest::addColumn("statusString"); + QTest::addColumn("contentLength"); + QTest::addColumn("downloadSize"); + + QTest::newRow("success-internal") << "http://" << QtNetworkSettings::serverName() << "/qtest/cgi-bin/echo.cgi" << ushort(80) << false << "7 bytes" << 200 << "OK" << 7 << 7; + QTest::newRow("failure-internal") << "http://" << QtNetworkSettings::serverName() << "/t" << ushort(80) << false << "Hello World" << 404 << "Not Found" << -1 << -1; +} + +void tst_QHttpNetworkConnection::post() +{ + QFETCH(QString, protocol); + QFETCH(QString, host); + QFETCH(QString, path); + QFETCH(ushort, port); + QFETCH(bool, encrypt); + QFETCH(QString, data); + QFETCH(int, statusCode); + QFETCH(QString, statusString); + QFETCH(int, contentLength); + QFETCH(int, downloadSize); + + QHttpNetworkConnection connection(host, port, encrypt); + QCOMPARE(connection.port(), port); + QCOMPARE(connection.hostName(), host); + QCOMPARE(connection.isSsl(), encrypt); + + QHttpNetworkRequest request(protocol + host + path, QHttpNetworkRequest::Post); + + QByteArray array = data.toLatin1(); + QNonContiguousByteDevice *bd = QNonContiguousByteDeviceFactory::create(&array); + bd->setParent(this); + request.setUploadByteDevice(bd); + + QHttpNetworkReply *reply = connection.sendRequest(request); + + QTime stopWatch; + stopWatch.start(); + forever { + QCoreApplication::instance()->processEvents(); + if (reply->bytesAvailable()) + break; + if (stopWatch.elapsed() >= 30000) + break; + } + + QCOMPARE(reply->statusCode(), statusCode); + QCOMPARE(reply->reasonPhrase(), statusString); + + qint64 cLen = reply->contentLength(); + if (contentLength != -1) { + // only check the content length if test expected it to be set + if (cLen==-1) { + // HTTP 1.1 server may respond with chunked encoding and in that + // case contentLength is not present in reply -> verify that it is the case + QByteArray transferEnc = reply->headerField("Transfer-Encoding"); + QCOMPARE(transferEnc, QByteArray("chunked")); + } else { + QCOMPARE(cLen, qint64(contentLength)); + } + } + + stopWatch.start(); + QByteArray ba; + do { + QCoreApplication::instance()->processEvents(); + while (reply->bytesAvailable()) + ba += reply->readAny(); + if (stopWatch.elapsed() >= 30000) + break; + } while (!reply->isFinished()); + + QVERIFY(reply->isFinished()); + //don't require fixed size for generated error pages + if (downloadSize != -1) + QCOMPARE(ba.size(), downloadSize); + //but do compare with content length if possible + if (cLen != -1) + QCOMPARE(ba.size(), (int)cLen); + + delete reply; +} + +void tst_QHttpNetworkConnection::_delete_data() +{ + // not tested yet +} + +void tst_QHttpNetworkConnection::_delete() +{ + QEXPECT_FAIL("", "not tested yet", Continue); + QVERIFY(false); +} + +void tst_QHttpNetworkConnection::trace_data() +{ + // not tested yet +} + +void tst_QHttpNetworkConnection::trace() +{ + QEXPECT_FAIL("", "not tested yet", Continue); + QVERIFY(false); +} + +void tst_QHttpNetworkConnection::_connect_data() +{ + // not tested yet +} + +void tst_QHttpNetworkConnection::_connect() +{ + QEXPECT_FAIL("", "not tested yet", Continue); + QVERIFY(false); +} + +void tst_QHttpNetworkConnection::challenge401(const QHttpNetworkRequest &request, + QAuthenticator *authenticator) +{ + Q_UNUSED(request) + + QHttpNetworkReply *reply = qobject_cast(sender()); + if (reply) { + QHttpNetworkConnection *c = reply->connection(); + + QVariant val = c->property("setCredentials"); + if (val.toBool()) { + QVariant user = c->property("username"); + QVariant password = c->property("password"); + authenticator->setUser(user.toString()); + authenticator->setPassword(password.toString()); + c->setProperty("setCredentials", false); + } + } +} + +void tst_QHttpNetworkConnection::get401_data() +{ + QTest::addColumn("protocol"); + QTest::addColumn("host"); + QTest::addColumn("path"); + QTest::addColumn("port"); + QTest::addColumn("encrypt"); + QTest::addColumn("setCredentials"); + QTest::addColumn("username"); + QTest::addColumn("password"); + QTest::addColumn("statusCode"); + + QTest::newRow("no-credentials") << "http://" << QtNetworkSettings::serverName() << "/qtest/rfcs-auth/index.html" << ushort(80) << false << false << "" << ""<<401; + QTest::newRow("invalid-credentials") << "http://" << QtNetworkSettings::serverName() << "/qtest/rfcs-auth/index.html" << ushort(80) << false << true << "test" << "test"<<401; + QTest::newRow("valid-credentials") << "http://" << QtNetworkSettings::serverName() << "/qtest/rfcs-auth/index.html" << ushort(80) << false << true << "httptest" << "httptest"<<200; + QTest::newRow("digest-authentication-invalid") << "http://" << QtNetworkSettings::serverName() << "/qtest/auth-digest/index.html" << ushort(80) << false << true << "wrong" << "wrong"<<401; + QTest::newRow("digest-authentication-valid") << "http://" << QtNetworkSettings::serverName() << "/qtest/auth-digest/index.html" << ushort(80) << false << true << "httptest" << "httptest"<<200; +} + +void tst_QHttpNetworkConnection::get401() +{ + QFETCH(QString, protocol); + QFETCH(QString, host); + QFETCH(QString, path); + QFETCH(ushort, port); + QFETCH(bool, encrypt); + QFETCH(bool, setCredentials); + QFETCH(QString, username); + QFETCH(QString, password); + QFETCH(int, statusCode); + + QHttpNetworkConnection connection(host, port, encrypt); + QCOMPARE(connection.port(), port); + QCOMPARE(connection.hostName(), host); + QCOMPARE(connection.isSsl(), encrypt); + connection.setProperty("setCredentials", setCredentials); + connection.setProperty("username", username); + connection.setProperty("password", password); + + QHttpNetworkRequest request(protocol + host + path); + QHttpNetworkReply *reply = connection.sendRequest(request); + connect(reply, SIGNAL(authenticationRequired(const QHttpNetworkRequest&, QAuthenticator *)), + SLOT(challenge401(const QHttpNetworkRequest&, QAuthenticator *))); + + finishedCalled = false; + finishedWithErrorCalled = false; + + connect(reply, SIGNAL(finished()), SLOT(finishedReply())); + connect(reply, SIGNAL(finishedWithError(QNetworkReply::NetworkError, const QString &)), + SLOT(finishedWithError(QNetworkReply::NetworkError, const QString &))); + + QTime stopWatch; + stopWatch.start(); + forever { + QCoreApplication::instance()->processEvents(); + if (finishedCalled) + break; + if (finishedWithErrorCalled) + break; + if (stopWatch.elapsed() >= 30000) + break; + } + QCOMPARE(reply->statusCode(), statusCode); + delete reply; +} + +#ifndef QT_NO_COMPRESS +void tst_QHttpNetworkConnection::compression_data() +{ + QTest::addColumn("protocol"); + QTest::addColumn("host"); + QTest::addColumn("path"); + QTest::addColumn("port"); + QTest::addColumn("encrypt"); + QTest::addColumn("statusCode"); + QTest::addColumn("statusString"); + QTest::addColumn("contentLength"); + QTest::addColumn("downloadSize"); + QTest::addColumn("autoCompress"); + QTest::addColumn("contentCoding"); + + QTest::newRow("success-autogzip-temp") << "http://" << QtNetworkSettings::serverName() << "/qtest/rfcs/rfc2616.html" << ushort(80) << false << 200 << "OK" << -1 << 418321 << true << ""; + QTest::newRow("success-nogzip-temp") << "http://" << QtNetworkSettings::serverName() << "/qtest/rfcs/rfc2616.html" << ushort(80) << false << 200 << "OK" << 418321 << 418321 << false << "identity"; + QTest::newRow("success-manualgzip-temp") << "http://" << QtNetworkSettings::serverName() << "/qtest/deflate/rfc2616.html" << ushort(80) << false << 200 << "OK" << 119124 << 119124 << false << "gzip"; + +} + +void tst_QHttpNetworkConnection::compression() +{ + QFETCH(QString, protocol); + QFETCH(QString, host); + QFETCH(QString, path); + QFETCH(ushort, port); + QFETCH(bool, encrypt); + QFETCH(int, statusCode); + QFETCH(QString, statusString); + QFETCH(int, contentLength); + QFETCH(int, downloadSize); + QFETCH(bool, autoCompress); + QFETCH(QString, contentCoding); + + QHttpNetworkConnection connection(host, port, encrypt); + QCOMPARE(connection.port(), port); + QCOMPARE(connection.hostName(), host); + QCOMPARE(connection.isSsl(), encrypt); + + QHttpNetworkRequest request(protocol + host + path); + if (!autoCompress) + request.setHeaderField("Accept-Encoding", contentCoding.toLatin1()); + QHttpNetworkReply *reply = connection.sendRequest(request); + QTime stopWatch; + stopWatch.start(); + forever { + QCoreApplication::instance()->processEvents(); + if (reply->bytesAvailable()) + break; + if (stopWatch.elapsed() >= 30000) + break; + } + + QCOMPARE(reply->statusCode(), statusCode); + QCOMPARE(reply->reasonPhrase(), statusString); + bool isLengthOk = (reply->contentLength() == qint64(contentLength) + || reply->contentLength() == qint64(downloadSize) + || reply->contentLength() == -1); //apache2 does not send content-length for compressed pages + + QVERIFY(isLengthOk); + + stopWatch.start(); + QByteArray ba; + do { + QCoreApplication::instance()->processEvents(); + while (reply->bytesAvailable()) + ba += reply->readAny(); + if (stopWatch.elapsed() >= 30000) + break; + } while (!reply->isFinished()); + + QVERIFY(reply->isFinished()); + QCOMPARE(ba.size(), downloadSize); + + delete reply; +} +#endif + +#ifndef QT_NO_OPENSSL +void tst_QHttpNetworkConnection::sslErrors(const QList &errors) +{ + Q_UNUSED(errors) + + QHttpNetworkReply *reply = qobject_cast(sender()); + if (reply) { + QHttpNetworkConnection *connection = reply->connection(); + + QVariant val = connection->property("ignoreFromSignal"); + if (val.toBool()) + connection->ignoreSslErrors(); + finishedWithErrorCalled = true; + } +} + +void tst_QHttpNetworkConnection::ignoresslerror_data() +{ + QTest::addColumn("protocol"); + QTest::addColumn("host"); + QTest::addColumn("path"); + QTest::addColumn("port"); + QTest::addColumn("encrypt"); + QTest::addColumn("ignoreInit"); + QTest::addColumn("ignoreFromSignal"); + QTest::addColumn("statusCode"); + + // This test will work only if the website has ssl errors. + // fluke's certificate is signed by a non-standard authority. + // Since we don't introduce that CA into the SSL verification chain, + // connecting should fail. + QTest::newRow("success-init") << "https://" << QtNetworkSettings::serverName() << "/" << ushort(443) << true << true << false << 200; + QTest::newRow("success-fromSignal") << "https://" << QtNetworkSettings::serverName() << "/" << ushort(443) << true << false << true << 200; + QTest::newRow("failure") << "https://" << QtNetworkSettings::serverName() << "/" << ushort(443) << true << false << false << 100; +} + +void tst_QHttpNetworkConnection::ignoresslerror() +{ + QFETCH(QString, protocol); + QFETCH(QString, host); + QFETCH(QString, path); + QFETCH(ushort, port); + QFETCH(bool, encrypt); + QFETCH(bool, ignoreInit); + QFETCH(bool, ignoreFromSignal); + QFETCH(int, statusCode); + + QHttpNetworkConnection connection(host, port, encrypt); + QCOMPARE(connection.port(), port); + QCOMPARE(connection.hostName(), host); + if (ignoreInit) + connection.ignoreSslErrors(); + QCOMPARE(connection.isSsl(), encrypt); + connection.setProperty("ignoreFromSignal", ignoreFromSignal); + + QHttpNetworkRequest request(protocol + host + path); + QHttpNetworkReply *reply = connection.sendRequest(request); + connect(reply, SIGNAL(sslErrors(const QList&)), + SLOT(sslErrors(const QList&))); + + finishedWithErrorCalled = false; + + connect(reply, SIGNAL(finished()), SLOT(finishedReply())); + + QTime stopWatch; + stopWatch.start(); + forever { + QCoreApplication::instance()->processEvents(); + if (reply->bytesAvailable()) + break; + if (statusCode == 100 && finishedWithErrorCalled) + break; + if (stopWatch.elapsed() >= 30000) + break; + } + QCOMPARE(reply->statusCode(), statusCode); + delete reply; +} +#endif + +#ifdef QT_NO_OPENSSL +Q_DECLARE_METATYPE(QNetworkReply::NetworkError) +void tst_QHttpNetworkConnection::nossl_data() +{ + QTest::addColumn("protocol"); + QTest::addColumn("host"); + QTest::addColumn("path"); + QTest::addColumn("port"); + QTest::addColumn("encrypt"); + QTest::addColumn("networkError"); + + QTest::newRow("protocol-error") << "https://" << QtNetworkSettings::serverName() << "/" << ushort(443) << true <processEvents(); + if (finishedWithErrorCalled) + break; + if (stopWatch.elapsed() >= 30000) + break; + } + QCOMPARE(netErrorCode, networkError); + delete reply; +} +#endif + + +void tst_QHttpNetworkConnection::getMultiple_data() +{ + QTest::addColumn("connectionCount"); + QTest::addColumn("pipeliningAllowed"); + // send 100 requests. apache will usually force-close after 100 requests in a single tcp connection + QTest::addColumn("requestCount"); + + QTest::newRow("6 connections, no pipelining, 100 requests") << quint16(6) << false << 100; + QTest::newRow("1 connection, no pipelining, 100 requests") << quint16(1) << false << 100; + QTest::newRow("6 connections, pipelining allowed, 100 requests") << quint16(6) << true << 100; + QTest::newRow("1 connection, pipelining allowed, 100 requests") << quint16(1) << true << 100; +} + +void tst_QHttpNetworkConnection::getMultiple() +{ + QFETCH(quint16, connectionCount); + QFETCH(bool, pipeliningAllowed); + QFETCH(int, requestCount); + + QHttpNetworkConnection connection(connectionCount, QtNetworkSettings::serverName()); + + QList requests; + QList replies; + + for (int i = 0; i < requestCount; i++) { + // depending on what you use the results will vary. + // for the "real" results, use a URL that has "internet latency" for you. Then (6 connections, pipelining) will win. + // for LAN latency, you will possibly get that (1 connection, no pipelining) is the fastest + QHttpNetworkRequest *request = new QHttpNetworkRequest("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"); + // located in Berlin: + //QHttpNetworkRequest *request = new QHttpNetworkRequest(QUrl("http://klinsmann.nokia.trolltech.de/~berlin/qtcreatorad.gif")); + if (pipeliningAllowed) + request->setPipeliningAllowed(true); + requests.append(request); + QHttpNetworkReply *reply = connection.sendRequest(*request); + replies.append(reply); + } + + QTime stopWatch; + stopWatch.start(); + int finishedCount = 0; + do { + QCoreApplication::instance()->processEvents(); + if (stopWatch.elapsed() >= 60000) + break; + + finishedCount = 0; + for (int i = 0; i < replies.length(); i++) + if (replies.at(i)->isFinished()) + finishedCount++; + + } while (finishedCount != replies.length()); + + // redundant + for (int i = 0; i < replies.length(); i++) + QVERIFY(replies.at(i)->isFinished()); + + qDebug() << "===" << stopWatch.elapsed() << "msec ==="; + + qDeleteAll(requests); + qDeleteAll(replies); +} + +void tst_QHttpNetworkConnection::getMultipleWithPipeliningAndMultiplePriorities() +{ + quint16 requestCount = 100; + + // use 2 connections. + QHttpNetworkConnection connection(2, QtNetworkSettings::serverName()); + + QList requests; + QList replies; + + for (int i = 0; i < requestCount; i++) { + QHttpNetworkRequest *request = 0; + if (i % 3) + request = new QHttpNetworkRequest("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt", QHttpNetworkRequest::Get); + else + request = new QHttpNetworkRequest("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt", QHttpNetworkRequest::Head); + + if (i % 2 || i % 3) + request->setPipeliningAllowed(true); + + if (i % 3) + request->setPriority(QHttpNetworkRequest::HighPriority); + else if (i % 5) + request->setPriority(QHttpNetworkRequest::NormalPriority); + else if (i % 7) + request->setPriority(QHttpNetworkRequest::LowPriority); + + requests.append(request); + QHttpNetworkReply *reply = connection.sendRequest(*request); + replies.append(reply); + } + + QTime stopWatch; + stopWatch.start(); + int finishedCount = 0; + do { + QCoreApplication::instance()->processEvents(); + if (stopWatch.elapsed() >= 60000) + break; + + finishedCount = 0; + for (int i = 0; i < replies.length(); i++) + if (replies.at(i)->isFinished()) + finishedCount++; + + } while (finishedCount != replies.length()); + + int pipelinedCount = 0; + for (int i = 0; i < replies.length(); i++) { + QVERIFY(replies.at(i)->isFinished()); + QVERIFY (!(replies.at(i)->request().isPipeliningAllowed() == false + && replies.at(i)->isPipeliningUsed())); + + if (replies.at(i)->isPipeliningUsed()) + pipelinedCount++; + } + + // We allow pipelining for every 2nd,3rd,4th,6th,8th,9th,10th etc request. + // Assume that half of the requests had been pipelined. + // (this is a very relaxed condition, when last measured 79 of 100 + // requests had been pipelined) + QVERIFY(pipelinedCount >= requestCount / 2); + + qDebug() << "===" << stopWatch.elapsed() << "msec ==="; + + qDeleteAll(requests); + qDeleteAll(replies); +} + +class GetMultipleWithPrioritiesReceiver : public QObject +{ + Q_OBJECT +public: + int highPrioReceived; + int lowPrioReceived; + int requestCount; + GetMultipleWithPrioritiesReceiver(int rq) : highPrioReceived(0), lowPrioReceived(0), requestCount(rq) { } +public Q_SLOTS: + void finishedSlot() { + QHttpNetworkReply *reply = (QHttpNetworkReply*) sender(); + if (reply->request().priority() == QHttpNetworkRequest::HighPriority) + highPrioReceived++; + else if (reply->request().priority() == QHttpNetworkRequest::LowPriority) + lowPrioReceived++; + else + QFAIL("Wrong priority!?"); + + QVERIFY(highPrioReceived + 7 >= lowPrioReceived); + + if (highPrioReceived + lowPrioReceived == requestCount) + QTestEventLoop::instance().exitLoop(); + } +}; + +void tst_QHttpNetworkConnection::getMultipleWithPriorities() +{ + quint16 requestCount = 100; + // use 2 connections. + QHttpNetworkConnection connection(2, QtNetworkSettings::serverName()); + GetMultipleWithPrioritiesReceiver receiver(requestCount); + QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"); + QList requests; + QList replies; + + for (int i = 0; i < requestCount; i++) { + QHttpNetworkRequest *request = 0; + if (i % 3) + request = new QHttpNetworkRequest(url, QHttpNetworkRequest::Get); + else + request = new QHttpNetworkRequest(url, QHttpNetworkRequest::Head); + + if (i % 2) + request->setPriority(QHttpNetworkRequest::HighPriority); + else + request->setPriority(QHttpNetworkRequest::LowPriority); + + requests.append(request); + QHttpNetworkReply *reply = connection.sendRequest(*request); + connect(reply, SIGNAL(finished()), &receiver, SLOT(finishedSlot())); + replies.append(reply); + } + + QTestEventLoop::instance().enterLoop(40); + QVERIFY(!QTestEventLoop::instance().timeout()); + + qDeleteAll(requests); + qDeleteAll(replies); +} + + +class GetEmptyWithPipeliningReceiver : public QObject +{ + Q_OBJECT +public: + int receivedCount; + int requestCount; + GetEmptyWithPipeliningReceiver(int rq) : receivedCount(0),requestCount(rq) { } +public Q_SLOTS: + void finishedSlot() { + QHttpNetworkReply *reply = (QHttpNetworkReply*) sender(); + Q_UNUSED(reply); + receivedCount++; + + if (receivedCount == requestCount) + QTestEventLoop::instance().exitLoop(); + } +}; + +void tst_QHttpNetworkConnection::getEmptyWithPipelining() +{ + quint16 requestCount = 50; + // use 2 connections. + QHttpNetworkConnection connection(2, QtNetworkSettings::serverName()); + GetEmptyWithPipeliningReceiver receiver(requestCount); + + QUrl url("http://" + QtNetworkSettings::serverName() + "/cgi-bin/echo.cgi"); // a get on this = getting an empty file + QList requests; + QList replies; + + for (int i = 0; i < requestCount; i++) { + QHttpNetworkRequest *request = 0; + request = new QHttpNetworkRequest(url, QHttpNetworkRequest::Get); + request->setPipeliningAllowed(true); + + requests.append(request); + QHttpNetworkReply *reply = connection.sendRequest(*request); + connect(reply, SIGNAL(finished()), &receiver, SLOT(finishedSlot())); + replies.append(reply); + } + + QTestEventLoop::instance().enterLoop(20); + QVERIFY(!QTestEventLoop::instance().timeout()); + + qDeleteAll(requests); + qDeleteAll(replies); +} + +class GetAndEverythingShouldBePipelinedReceiver : public QObject +{ + Q_OBJECT +public: + int receivedCount; + int requestCount; + GetAndEverythingShouldBePipelinedReceiver(int rq) : receivedCount(0),requestCount(rq) { } +public Q_SLOTS: + void finishedSlot() { + QHttpNetworkReply *reply = (QHttpNetworkReply*) sender(); + Q_UNUSED(reply); + receivedCount++; + + if (receivedCount == requestCount) + QTestEventLoop::instance().exitLoop(); + } +}; + +void tst_QHttpNetworkConnection::getAndEverythingShouldBePipelined() +{ + quint16 requestCount = 100; + // use 1 connection. + QHttpNetworkConnection connection(1, QtNetworkSettings::serverName()); + QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"); + QList requests; + QList replies; + + GetAndEverythingShouldBePipelinedReceiver receiver(requestCount); + + for (int i = 0; i < requestCount; i++) { + QHttpNetworkRequest *request = 0; + request = new QHttpNetworkRequest(url, QHttpNetworkRequest::Get); + request->setPipeliningAllowed(true); + requests.append(request); + QHttpNetworkReply *reply = connection.sendRequest(*request); + connect(reply, SIGNAL(finished()), &receiver, SLOT(finishedSlot())); + replies.append(reply); + } + QTestEventLoop::instance().enterLoop(40); + QVERIFY(!QTestEventLoop::instance().timeout()); + + qDeleteAll(requests); + qDeleteAll(replies); + +} + + +void tst_QHttpNetworkConnection::getAndThenDeleteObject_data() +{ + QTest::addColumn("replyFirst"); + + QTest::newRow("delete-reply-first") << true; + QTest::newRow("delete-connection-first") << false; +} + +void tst_QHttpNetworkConnection::getAndThenDeleteObject() +{ + // yes, this will leak if the testcase fails. I don't care. It must not fail then :P + QHttpNetworkConnection *connection = new QHttpNetworkConnection(QtNetworkSettings::serverName()); + QHttpNetworkRequest request("http://" + QtNetworkSettings::serverName() + "/qtest/bigfile"); + QHttpNetworkReply *reply = connection->sendRequest(request); + reply->setDownstreamLimited(true); + + QTime stopWatch; + stopWatch.start(); + forever { + QCoreApplication::instance()->processEvents(); + if (reply->bytesAvailable()) + break; + if (stopWatch.elapsed() >= 30000) + break; + } + + QVERIFY(reply->bytesAvailable()); + QCOMPARE(reply->statusCode() ,200); + QVERIFY(!reply->isFinished()); // must not be finished + + QFETCH(bool, replyFirst); + + if (replyFirst) { + delete reply; + delete connection; + } else { + delete connection; + delete reply; + } +} + + + +QTEST_MAIN(tst_QHttpNetworkConnection) +#include "tst_qhttpnetworkconnection.moc" diff --git a/tests/auto/network/access/qhttpnetworkreply/.gitignore b/tests/auto/network/access/qhttpnetworkreply/.gitignore new file mode 100644 index 0000000000..fdd4e2be86 --- /dev/null +++ b/tests/auto/network/access/qhttpnetworkreply/.gitignore @@ -0,0 +1 @@ +tst_qhttpnetworkreply diff --git a/tests/auto/network/access/qhttpnetworkreply/qhttpnetworkreply.pro b/tests/auto/network/access/qhttpnetworkreply/qhttpnetworkreply.pro new file mode 100644 index 0000000000..8ce9f6f760 --- /dev/null +++ b/tests/auto/network/access/qhttpnetworkreply/qhttpnetworkreply.pro @@ -0,0 +1,7 @@ +load(qttest_p4) +SOURCES += tst_qhttpnetworkreply.cpp +INCLUDEPATH += $$QT_SOURCE_TREE/src/3rdparty/zlib +requires(contains(QT_CONFIG,private_tests)) + +QT = core-private network-private +symbian: TARGET.CAPABILITY = NetworkServices diff --git a/tests/auto/network/access/qhttpnetworkreply/tst_qhttpnetworkreply.cpp b/tests/auto/network/access/qhttpnetworkreply/tst_qhttpnetworkreply.cpp new file mode 100644 index 0000000000..513b20ec82 --- /dev/null +++ b/tests/auto/network/access/qhttpnetworkreply/tst_qhttpnetworkreply.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include "private/qhttpnetworkconnection_p.h" + +class tst_QHttpNetworkReply: public QObject +{ + Q_OBJECT +private Q_SLOTS: + void init(); + void cleanup(); + void initTestCase(); + void cleanupTestCase(); + + void parseHeader_data(); + void parseHeader(); +}; + + +void tst_QHttpNetworkReply::initTestCase() +{ +} + +void tst_QHttpNetworkReply::cleanupTestCase() +{ +} + +void tst_QHttpNetworkReply::init() +{ +} + +void tst_QHttpNetworkReply::cleanup() +{ +} + +void tst_QHttpNetworkReply::parseHeader_data() +{ + QTest::addColumn("headers"); + QTest::addColumn("fields"); + QTest::addColumn("values"); + + QTest::newRow("empty-field") << QByteArray("Set-Cookie: \r\n") + << (QStringList() << "Set-Cookie") + << (QStringList() << ""); + QTest::newRow("single-field") << QByteArray("Content-Type: text/html; charset=utf-8\r\n") + << (QStringList() << "Content-Type") + << (QStringList() << "text/html; charset=utf-8"); + QTest::newRow("single-field-continued") << QByteArray("Content-Type: text/html;\r\n" + " charset=utf-8\r\n") + << (QStringList() << "Content-Type") + << (QStringList() << "text/html; charset=utf-8"); + + QTest::newRow("multi-field") << QByteArray("Content-Type: text/html; charset=utf-8\r\n" + "Content-Length: 1024\r\n" + "Content-Encoding: gzip\r\n") + << (QStringList() << "Content-Type" << "Content-Length" << "Content-Encoding") + << (QStringList() << "text/html; charset=utf-8" << "1024" << "gzip"); + QTest::newRow("multi-field-with-emtpy") << QByteArray("Content-Type: text/html; charset=utf-8\r\n" + "Content-Length: 1024\r\n" + "Set-Cookie: \r\n" + "Content-Encoding: gzip\r\n") + << (QStringList() << "Content-Type" << "Content-Length" << "Set-Cookie" << "Content-Encoding") + << (QStringList() << "text/html; charset=utf-8" << "1024" << "" << "gzip"); + + QTest::newRow("lws-field") << QByteArray("Content-Type: text/html; charset=utf-8\r\n" + "Content-Length:\r\n 1024\r\n" + "Content-Encoding: gzip\r\n") + << (QStringList() << "Content-Type" << "Content-Length" << "Content-Encoding") + << (QStringList() << "text/html; charset=utf-8" << "1024" << "gzip"); + + QTest::newRow("duplicated-field") << QByteArray("Vary: Accept-Language\r\n" + "Vary: Cookie\r\n" + "Vary: User-Agent\r\n") + << (QStringList() << "Vary") + << (QStringList() << "Accept-Language, Cookie, User-Agent"); +} + +void tst_QHttpNetworkReply::parseHeader() +{ + QFETCH(QByteArray, headers); + QFETCH(QStringList, fields); + QFETCH(QStringList, values); + + QHttpNetworkReply reply; + reply.parseHeader(headers); + for (int i = 0; i < fields.count(); ++i) { + //qDebug() << "field" << fields.at(i) << "value" << reply.headerField(fields.at(i)) << "expected" << values.at(i); + QString field = reply.headerField(fields.at(i).toLatin1()); + QCOMPARE(field, values.at(i)); + } +} + +QTEST_MAIN(tst_QHttpNetworkReply) +#include "tst_qhttpnetworkreply.moc" diff --git a/tests/auto/network/access/qnetworkaccessmanager/qnetworkaccessmanager.pro b/tests/auto/network/access/qnetworkaccessmanager/qnetworkaccessmanager.pro new file mode 100644 index 0000000000..3ccbffbde8 --- /dev/null +++ b/tests/auto/network/access/qnetworkaccessmanager/qnetworkaccessmanager.pro @@ -0,0 +1,6 @@ +load(qttest_p4) +SOURCES += tst_qnetworkaccessmanager.cpp +QT = core network + +symbian: TARGET.CAPABILITY = NetworkServices + diff --git a/tests/auto/network/access/qnetworkaccessmanager/tst_qnetworkaccessmanager.cpp b/tests/auto/network/access/qnetworkaccessmanager/tst_qnetworkaccessmanager.cpp new file mode 100644 index 0000000000..14804b5187 --- /dev/null +++ b/tests/auto/network/access/qnetworkaccessmanager/tst_qnetworkaccessmanager.cpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include +#include + +#include + +Q_DECLARE_METATYPE(QNetworkAccessManager::NetworkAccessibility); + +class tst_QNetworkAccessManager : public QObject +{ + Q_OBJECT + +public: + tst_QNetworkAccessManager(); + +private slots: + void networkAccessible(); +}; + +tst_QNetworkAccessManager::tst_QNetworkAccessManager() +{ +} + +void tst_QNetworkAccessManager::networkAccessible() +{ + QNetworkAccessManager manager; + + qRegisterMetaType("QNetworkAccessManager::NetworkAccessibility"); + + QSignalSpy spy(&manager, + SIGNAL(networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility))); + + QCOMPARE(manager.networkAccessible(), QNetworkAccessManager::UnknownAccessibility); + + manager.setNetworkAccessible(QNetworkAccessManager::NotAccessible); + + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.takeFirst().at(0).value(), + QNetworkAccessManager::NotAccessible); + QCOMPARE(manager.networkAccessible(), QNetworkAccessManager::NotAccessible); + + manager.setNetworkAccessible(QNetworkAccessManager::Accessible); + + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.takeFirst().at(0).value(), + QNetworkAccessManager::UnknownAccessibility); + QCOMPARE(manager.networkAccessible(), QNetworkAccessManager::UnknownAccessibility); + + QNetworkConfigurationManager configManager; + QNetworkConfiguration defaultConfig = configManager.defaultConfiguration(); + if (defaultConfig.isValid()) { + manager.setConfiguration(defaultConfig); + + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.takeFirst().at(0).value(), + QNetworkAccessManager::Accessible); + QCOMPARE(manager.networkAccessible(), QNetworkAccessManager::Accessible); + + manager.setNetworkAccessible(QNetworkAccessManager::NotAccessible); + + QCOMPARE(spy.count(), 1); + QCOMPARE(QNetworkAccessManager::NetworkAccessibility(spy.takeFirst().at(0).toInt()), + QNetworkAccessManager::NotAccessible); + QCOMPARE(manager.networkAccessible(), QNetworkAccessManager::NotAccessible); + } +} + +QTEST_MAIN(tst_QNetworkAccessManager) +#include "tst_qnetworkaccessmanager.moc" diff --git a/tests/auto/network/access/qnetworkcachemetadata/.gitignore b/tests/auto/network/access/qnetworkcachemetadata/.gitignore new file mode 100644 index 0000000000..76f9e574b1 --- /dev/null +++ b/tests/auto/network/access/qnetworkcachemetadata/.gitignore @@ -0,0 +1 @@ +tst_qnetworkcachemetadata diff --git a/tests/auto/network/access/qnetworkcachemetadata/qnetworkcachemetadata.pro b/tests/auto/network/access/qnetworkcachemetadata/qnetworkcachemetadata.pro new file mode 100644 index 0000000000..ae0941e964 --- /dev/null +++ b/tests/auto/network/access/qnetworkcachemetadata/qnetworkcachemetadata.pro @@ -0,0 +1,7 @@ +load(qttest_p4) +QT -= gui +QT += network +SOURCES += tst_qnetworkcachemetadata.cpp + +symbian: TARGET.CAPABILITY = NetworkServices + diff --git a/tests/auto/network/access/qnetworkcachemetadata/tst_qnetworkcachemetadata.cpp b/tests/auto/network/access/qnetworkcachemetadata/tst_qnetworkcachemetadata.cpp new file mode 100644 index 0000000000..a5b63bd7d5 --- /dev/null +++ b/tests/auto/network/access/qnetworkcachemetadata/tst_qnetworkcachemetadata.cpp @@ -0,0 +1,373 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include + +#define EXAMPLE_URL "http://user:pass@www.example.com/#foo" + +class tst_QNetworkCacheMetaData : public QObject +{ + Q_OBJECT + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private slots: + void qnetworkcachemetadata_data(); + void qnetworkcachemetadata(); + + void expirationDate_data(); + void expirationDate(); + void isValid_data(); + void isValid(); + void lastModified_data(); + void lastModified(); + void operatorEqual_data(); + void operatorEqual(); + void operatorEqualEqual_data(); + void operatorEqualEqual(); + void rawHeaders_data(); + void rawHeaders(); + void saveToDisk_data(); + void saveToDisk(); + void url_data(); + void url(); + + void stream(); +}; + +// Subclass that exposes the protected functions. +class SubQNetworkCacheMetaData : public QNetworkCacheMetaData +{ +public:}; + +// This will be called before the first test function is executed. +// It is only called once. +void tst_QNetworkCacheMetaData::initTestCase() +{ +} + +// This will be called after the last test function is executed. +// It is only called once. +void tst_QNetworkCacheMetaData::cleanupTestCase() +{ +} + +// This will be called before each test function is executed. +void tst_QNetworkCacheMetaData::init() +{ +} + +// This will be called after every test function. +void tst_QNetworkCacheMetaData::cleanup() +{ +} + +void tst_QNetworkCacheMetaData::qnetworkcachemetadata_data() +{ +} + +void tst_QNetworkCacheMetaData::qnetworkcachemetadata() +{ + QNetworkCacheMetaData data; + QCOMPARE(data.expirationDate(), QDateTime()); + QCOMPARE(data.isValid(), false); + QCOMPARE(data.lastModified(), QDateTime()); + QCOMPARE(data.operator!=(QNetworkCacheMetaData()), false); + QNetworkCacheMetaData metaData; + QCOMPARE(data.operator=(metaData), QNetworkCacheMetaData()); + QCOMPARE(data.operator==(QNetworkCacheMetaData()), true); + QCOMPARE(data.rawHeaders(), QNetworkCacheMetaData::RawHeaderList()); + QCOMPARE(data.saveToDisk(), true); + QCOMPARE(data.url(), QUrl()); + data.setExpirationDate(QDateTime()); + data.setLastModified(QDateTime()); + data.setRawHeaders(QNetworkCacheMetaData::RawHeaderList()); + data.setSaveToDisk(false); + data.setUrl(QUrl()); +} + +void tst_QNetworkCacheMetaData::expirationDate_data() +{ + QTest::addColumn("expirationDate"); + QTest::newRow("null") << QDateTime(); + QTest::newRow("now") << QDateTime::currentDateTime(); +} + +// public QDateTime expirationDate() const +void tst_QNetworkCacheMetaData::expirationDate() +{ + QFETCH(QDateTime, expirationDate); + + SubQNetworkCacheMetaData data; + + data.setExpirationDate(expirationDate); + QCOMPARE(data.expirationDate(), expirationDate); +} + +Q_DECLARE_METATYPE(QNetworkCacheMetaData) +void tst_QNetworkCacheMetaData::isValid_data() +{ + QTest::addColumn("data"); + QTest::addColumn("isValid"); + + QNetworkCacheMetaData metaData; + QTest::newRow("null") << metaData << false; + + QNetworkCacheMetaData data1; + data1.setUrl(QUrl(EXAMPLE_URL)); + QTest::newRow("valid-1") << data1 << true; + + QNetworkCacheMetaData data2; + QNetworkCacheMetaData::RawHeaderList headers; + headers.append(QNetworkCacheMetaData::RawHeader("foo", "Bar")); + data2.setRawHeaders(headers); + QTest::newRow("valid-2") << data2 << true; + + QNetworkCacheMetaData data3; + data3.setLastModified(QDateTime::currentDateTime()); + QTest::newRow("valid-3") << data3 << true; + + QNetworkCacheMetaData data4; + data4.setExpirationDate(QDateTime::currentDateTime()); + QTest::newRow("valid-4") << data4 << true; + + QNetworkCacheMetaData data5; + data5.setSaveToDisk(false); + QTest::newRow("valid-5") << data5 << true; +} + +// public bool isValid() const +void tst_QNetworkCacheMetaData::isValid() +{ + QFETCH(QNetworkCacheMetaData, data); + QFETCH(bool, isValid); + + QCOMPARE(data.isValid(), isValid); +} + +void tst_QNetworkCacheMetaData::lastModified_data() +{ + QTest::addColumn("lastModified"); + QTest::newRow("null") << QDateTime(); + QTest::newRow("now") << QDateTime::currentDateTime(); +} + +// public QDateTime lastModified() const +void tst_QNetworkCacheMetaData::lastModified() +{ + QFETCH(QDateTime, lastModified); + + SubQNetworkCacheMetaData data; + + data.setLastModified(lastModified); + QCOMPARE(data.lastModified(), lastModified); +} + +void tst_QNetworkCacheMetaData::operatorEqual_data() +{ + QTest::addColumn("other"); + QTest::newRow("null") << QNetworkCacheMetaData(); + + QNetworkCacheMetaData data; + data.setUrl(QUrl(EXAMPLE_URL)); + QNetworkCacheMetaData::RawHeaderList headers; + headers.append(QNetworkCacheMetaData::RawHeader("foo", "Bar")); + data.setRawHeaders(headers); + data.setLastModified(QDateTime::currentDateTime()); + data.setExpirationDate(QDateTime::currentDateTime()); + data.setSaveToDisk(false); + QTest::newRow("valid") << data; +} + +// public QNetworkCacheMetaData& operator=(QNetworkCacheMetaData const& other) +void tst_QNetworkCacheMetaData::operatorEqual() +{ + QFETCH(QNetworkCacheMetaData, other); + + QNetworkCacheMetaData data = other; + + QCOMPARE(data, other); +} + +void tst_QNetworkCacheMetaData::operatorEqualEqual_data() +{ + QTest::addColumn("a"); + QTest::addColumn("b"); + QTest::addColumn("operatorEqualEqual"); + QTest::newRow("null") << QNetworkCacheMetaData() << QNetworkCacheMetaData() << true; + + QNetworkCacheMetaData data1; + data1.setUrl(QUrl(EXAMPLE_URL)); + QTest::newRow("valid-1-1") << data1 << QNetworkCacheMetaData() << false; + QTest::newRow("valid-1-2") << data1 << data1 << true; + + QNetworkCacheMetaData data2; + QNetworkCacheMetaData::RawHeaderList headers; + headers.append(QNetworkCacheMetaData::RawHeader("foo", "Bar")); + data2.setRawHeaders(headers); + QTest::newRow("valid-2-1") << data2 << QNetworkCacheMetaData() << false; + QTest::newRow("valid-2-2") << data2 << data2 << true; + QTest::newRow("valid-2-3") << data2 << data1 << false; + + QNetworkCacheMetaData data3; + data3.setLastModified(QDateTime::currentDateTime()); + QTest::newRow("valid-3-1") << data3 << QNetworkCacheMetaData() << false; + QTest::newRow("valid-3-2") << data3 << data3 << true; + QTest::newRow("valid-3-3") << data3 << data1 << false; + QTest::newRow("valid-3-4") << data3 << data2 << false; + + QNetworkCacheMetaData data4; + data4.setExpirationDate(QDateTime::currentDateTime()); + QTest::newRow("valid-4-1") << data4 << QNetworkCacheMetaData() << false; + QTest::newRow("valid-4-2") << data4 << data4 << true; + QTest::newRow("valid-4-3") << data4 << data1 << false; + QTest::newRow("valid-4-4") << data4 << data2 << false; + QTest::newRow("valid-4-5") << data4 << data3 << false; + + QNetworkCacheMetaData data5; + data5.setSaveToDisk(false); + QTest::newRow("valid-5-1") << data5 << QNetworkCacheMetaData() << false; + QTest::newRow("valid-5-2") << data5 << data5 << true; + QTest::newRow("valid-5-3") << data5 << data1 << false; + QTest::newRow("valid-5-4") << data5 << data2 << false; + QTest::newRow("valid-5-5") << data5 << data3 << false; + QTest::newRow("valid-5-6") << data5 << data4 << false; +} + +// public bool operator==(QNetworkCacheMetaData const& other) const +void tst_QNetworkCacheMetaData::operatorEqualEqual() +{ + QFETCH(QNetworkCacheMetaData, a); + QFETCH(QNetworkCacheMetaData, b); + QFETCH(bool, operatorEqualEqual); + + QCOMPARE(a == b, operatorEqualEqual); +} + +Q_DECLARE_METATYPE(QNetworkCacheMetaData::RawHeaderList) +void tst_QNetworkCacheMetaData::rawHeaders_data() +{ + QTest::addColumn("rawHeaders"); + QTest::newRow("null") << QNetworkCacheMetaData::RawHeaderList(); + QNetworkCacheMetaData::RawHeaderList headers; + headers.append(QNetworkCacheMetaData::RawHeader("foo", "Bar")); + QTest::newRow("valie") << headers; +} + +// public QNetworkCacheMetaData::RawHeaderList rawHeaders() const +void tst_QNetworkCacheMetaData::rawHeaders() +{ + QFETCH(QNetworkCacheMetaData::RawHeaderList, rawHeaders); + + SubQNetworkCacheMetaData data; + + data.setRawHeaders(rawHeaders); + QCOMPARE(data.rawHeaders(), rawHeaders); +} + +void tst_QNetworkCacheMetaData::saveToDisk_data() +{ + QTest::addColumn("saveToDisk"); + QTest::newRow("false") << false; + QTest::newRow("true") << true; +} + +// public bool saveToDisk() const +void tst_QNetworkCacheMetaData::saveToDisk() +{ + QFETCH(bool, saveToDisk); + + SubQNetworkCacheMetaData data; + + data.setSaveToDisk(saveToDisk); + QCOMPARE(data.saveToDisk(), saveToDisk); +} + +void tst_QNetworkCacheMetaData::url_data() +{ + QTest::addColumn("url"); + QTest::addColumn("expected"); + QTest::newRow("null") << QUrl() << QUrl(); + QTest::newRow("valid") << QUrl(EXAMPLE_URL) << QUrl("http://user@www.example.com/"); +} + +// public QUrl url() const +void tst_QNetworkCacheMetaData::url() +{ + QFETCH(QUrl, url); + QFETCH(QUrl, expected); + + SubQNetworkCacheMetaData data; + data.setUrl(url); + QCOMPARE(data.url(), expected); +} + +void tst_QNetworkCacheMetaData::stream() +{ + QNetworkCacheMetaData data; + data.setUrl(QUrl(EXAMPLE_URL)); + QNetworkCacheMetaData::RawHeaderList headers; + headers.append(QNetworkCacheMetaData::RawHeader("foo", "Bar")); + data.setRawHeaders(headers); + data.setLastModified(QDateTime::currentDateTime()); + data.setExpirationDate(QDateTime::currentDateTime()); + data.setSaveToDisk(false); + + QBuffer buffer; + buffer.open(QIODevice::ReadWrite); + QDataStream stream(&buffer); + stream << data; + + buffer.seek(0); + QNetworkCacheMetaData data2; + stream >> data2; + QCOMPARE(data2, data); +} + +QTEST_MAIN(tst_QNetworkCacheMetaData) +#include "tst_qnetworkcachemetadata.moc" + diff --git a/tests/auto/network/access/qnetworkcookie/.gitignore b/tests/auto/network/access/qnetworkcookie/.gitignore new file mode 100644 index 0000000000..90d1081d12 --- /dev/null +++ b/tests/auto/network/access/qnetworkcookie/.gitignore @@ -0,0 +1 @@ +tst_qnetworkcookie diff --git a/tests/auto/network/access/qnetworkcookie/qnetworkcookie.pro b/tests/auto/network/access/qnetworkcookie/qnetworkcookie.pro new file mode 100644 index 0000000000..2f31138daf --- /dev/null +++ b/tests/auto/network/access/qnetworkcookie/qnetworkcookie.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +SOURCES += tst_qnetworkcookie.cpp + +QT = core network +symbian: TARGET.CAPABILITY = NetworkServices diff --git a/tests/auto/network/access/qnetworkcookie/tst_qnetworkcookie.cpp b/tests/auto/network/access/qnetworkcookie/tst_qnetworkcookie.cpp new file mode 100644 index 0000000000..a83f6dda91 --- /dev/null +++ b/tests/auto/network/access/qnetworkcookie/tst_qnetworkcookie.cpp @@ -0,0 +1,733 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include +#include + + +class tst_QNetworkCookie: public QObject +{ + Q_OBJECT + +private slots: + void getterSetter(); + + void parseSingleCookie_data(); + void parseSingleCookie(); + + void parseMultipleCookies_data(); + void parseMultipleCookies(); +}; + +QT_BEGIN_NAMESPACE + +namespace QTest { + template<> + char *toString(const QNetworkCookie &cookie) + { + return qstrdup(cookie.toRawForm()); + } + + template<> + char *toString(const QList &list) + { + QString result = "QList("; + bool first = true; + foreach (QNetworkCookie cookie, list) { + if (!first) + result += ", "; + first = false; + result += QString::fromLatin1("QNetworkCookie(%1)").arg(QLatin1String(cookie.toRawForm())); + } + + return qstrdup(result.append(')').toLocal8Bit()); + } +} + +QT_END_NAMESPACE + +void tst_QNetworkCookie::getterSetter() +{ + QNetworkCookie cookie; + QNetworkCookie otherCookie; + + QVERIFY(cookie == otherCookie); + QCOMPARE(cookie, otherCookie); + QVERIFY(!(cookie != otherCookie)); + + QVERIFY(!cookie.isSecure()); + QVERIFY(cookie.isSessionCookie()); + QVERIFY(!cookie.expirationDate().isValid()); + QVERIFY(cookie.domain().isEmpty()); + QVERIFY(cookie.path().isEmpty()); + QVERIFY(cookie.name().isEmpty()); + QVERIFY(cookie.value().isEmpty()); + + // change something + cookie.setName("foo"); + QVERIFY(!(cookie == otherCookie)); + QVERIFY(cookie != otherCookie); + + // test getters and setters: + QCOMPARE(cookie.name(), QByteArray("foo")); + cookie.setName(0); + QVERIFY(cookie.name().isEmpty()); + + cookie.setValue("bar"); + QCOMPARE(cookie.value(), QByteArray("bar")); + cookie.setValue(0); + QVERIFY(cookie.value().isEmpty()); + + cookie.setPath("/"); + QCOMPARE(cookie.path(), QString("/")); + cookie.setPath(QString()); + QVERIFY(cookie.path().isEmpty()); + + cookie.setDomain(".tld"); + QCOMPARE(cookie.domain(), QString(".tld")); + cookie.setDomain(QString()); + QVERIFY(cookie.domain().isEmpty()); + + QDateTime now = QDateTime::currentDateTime(); + cookie.setExpirationDate(now); + QCOMPARE(cookie.expirationDate(), now); + QVERIFY(!cookie.isSessionCookie()); + cookie.setExpirationDate(QDateTime()); + QVERIFY(!cookie.expirationDate().isValid()); + QVERIFY(cookie.isSessionCookie()); + + cookie.setSecure(true); + QVERIFY(cookie.isSecure()); + cookie.setSecure(false); + QVERIFY(!cookie.isSecure()); + + QVERIFY(cookie == otherCookie); +} + +void tst_QNetworkCookie::parseSingleCookie_data() +{ + QTest::addColumn("cookieString"); + QTest::addColumn("expectedCookie"); + + QNetworkCookie cookie; + cookie.setName("a"); + QTest::newRow("basic") << "a=" << cookie; + QTest::newRow("basic2") << " a=" << cookie; + QTest::newRow("basic3") << "a= " << cookie; + QTest::newRow("basic4") << " a= " << cookie; + QTest::newRow("basic5") << " a= ;" << cookie; + QTest::newRow("basic6") << " a=; " << cookie; + QTest::newRow("basic7") << " a =" << cookie; + QTest::newRow("basic8") << " a = " << cookie; + + cookie.setValue("b"); + QTest::newRow("with-value") << "a=b" << cookie; + QTest::newRow("with-value2") << " a=b" << cookie; + QTest::newRow("with-value3") << "a=b " << cookie; + QTest::newRow("with-value4") << " a=b " << cookie; + QTest::newRow("with-value4") << " a=b ;" << cookie; + QTest::newRow("with-value5") << "a =b" << cookie; + QTest::newRow("with-value6") << "a= b" << cookie; + QTest::newRow("with-value7") << "a = b" << cookie; + QTest::newRow("with-value8") << "a = b " << cookie; + + cookie.setValue("\",\""); + QTest::newRow("with-value-with-special1") << "a = \",\" " << cookie; + cookie.setValue("\";\""); + QTest::newRow("with-value-with-special2") << "a = \";\" " << cookie; + cookie.setValue("\" \""); + QTest::newRow("with-value-with-special3") << "a = \" \" " << cookie; + cookie.setValue("\"\\\"\""); + QTest::newRow("with-value-with-special4") << "a = \"\\\"\" " << cookie; + cookie.setValue("\"\\\"a, b; c\\\"\""); + QTest::newRow("with-value-with-special5") << "a = \"\\\"a, b; c\\\"\"" << cookie; + + cookie.setValue("b c"); + QTest::newRow("with-value-with-whitespace") << "a = b c" << cookie; + + cookie.setValue("\"b\""); + QTest::newRow("quoted-value") << "a = \"b\"" << cookie; + cookie.setValue("\"b c\""); + QTest::newRow("quoted-value-with-whitespace") << "a = \"b c\"" << cookie; + + cookie.setValue("b"); + cookie.setSecure(true); + QTest::newRow("secure") << "a=b;secure" << cookie; + QTest::newRow("secure2") << "a=b;secure " << cookie; + QTest::newRow("secure3") << "a=b; secure" << cookie; + QTest::newRow("secure4") << "a=b; secure " << cookie; + QTest::newRow("secure5") << "a=b ;secure" << cookie; + QTest::newRow("secure6") << "a=b ;secure " << cookie; + QTest::newRow("secure7") << "a=b ; secure " << cookie; + QTest::newRow("secure8") << "a=b; Secure" << cookie; + + cookie.setSecure(false); + cookie.setHttpOnly(true); + QTest::newRow("httponly") << "a=b;httponly" << cookie; + QTest::newRow("httponly2") << "a=b;HttpOnly " << cookie; + QTest::newRow("httponly3") << "a=b; httpOnly" << cookie; + QTest::newRow("httponly4") << "a=b; HttpOnly " << cookie; + QTest::newRow("httponly5") << "a=b ;HttpOnly" << cookie; + QTest::newRow("httponly6") << "a=b ;httponly " << cookie; + QTest::newRow("httponly7") << "a=b ; HttpOnly " << cookie; + QTest::newRow("httponly8") << "a=b; Httponly" << cookie; + + cookie.setHttpOnly(false); + cookie.setPath("/"); + QTest::newRow("path1") << "a=b;path=/" << cookie; + QTest::newRow("path2") << "a=b; path=/" << cookie; + QTest::newRow("path3") << "a=b;path=/ " << cookie; + QTest::newRow("path4") << "a=b;path =/ " << cookie; + QTest::newRow("path5") << "a=b;path= / " << cookie; + QTest::newRow("path6") << "a=b;path = / " << cookie; + QTest::newRow("path7") << "a=b;Path = / " << cookie; + QTest::newRow("path8") << "a=b; PATH = / " << cookie; + + cookie.setPath("/foo"); + QTest::newRow("path9") << "a=b;path=/foo" << cookie; + + // some weird paths: + cookie.setPath("/with spaces"); + QTest::newRow("path-with-spaces") << "a=b;path=/with%20spaces" << cookie; + QTest::newRow("path-with-spaces2") << "a=b; path=/with%20spaces " << cookie; + QTest::newRow("path-with-spaces3") << "a=b; path=\"/with spaces\"" << cookie; + QTest::newRow("path-with-spaces4") << "a=b; path = \"/with spaces\" " << cookie; + + cookie.setPath("/with\"Quotes"); + QTest::newRow("path-with-quotes") << "a=b; path = /with%22Quotes" << cookie; + QTest::newRow("path-with-quotes2") << "a=b; path = \"/with\\\"Quotes\"" << cookie; + + cookie.setPath(QString::fromUtf8("/R\303\251sum\303\251")); + QTest::newRow("path-with-utf8") << "a=b;path=/R\303\251sum\303\251" << cookie; + QTest::newRow("path-with-utf8-2") << "a=b;path=/R%C3%A9sum%C3%A9" << cookie; + + cookie.setPath(QString()); + cookie.setDomain("qt.nokia.com"); + QTest::newRow("plain-domain1") << "a=b;domain=qt.nokia.com" << cookie; + QTest::newRow("plain-domain2") << "a=b; domain=qt.nokia.com " << cookie; + QTest::newRow("plain-domain3") << "a=b;domain=QT.NOKIA.COM" << cookie; + QTest::newRow("plain-domain4") << "a=b;DOMAIN = QT.NOKIA.COM" << cookie; + + cookie.setDomain(".qt.nokia.com"); + QTest::newRow("dot-domain1") << "a=b;domain=.qt.nokia.com" << cookie; + QTest::newRow("dot-domain2") << "a=b; domain=.qt.nokia.com" << cookie; + QTest::newRow("dot-domain3") << "a=b; domain=.QT.NOKIA.COM" << cookie; + QTest::newRow("dot-domain4") << "a=b; Domain = .QT.NOKIA.COM" << cookie; + + cookie.setDomain(QString::fromUtf8(".d\303\270gn\303\245pent.troll.no")); + QTest::newRow("idn-domain1") << "a=b;domain=.xn--dgnpent-gxa2o.troll.no" << cookie; + QTest::newRow("idn-domain2") << "a=b;domain=.d\303\270gn\303\245pent.troll.no" << cookie; + QTest::newRow("idn-domain3") << "a=b;domain=.XN--DGNPENT-GXA2O.TROLL.NO" << cookie; + QTest::newRow("idn-domain4") << "a=b;domain=.D\303\230GN\303\205PENT.troll.NO" << cookie; + + cookie.setDomain(".qt.nokia.com"); + cookie.setPath("/"); + QTest::newRow("two-fields") << "a=b;domain=.qt.nokia.com;path=/" << cookie; + QTest::newRow("two-fields2") << "a=b; domain=.qt.nokia.com; path=/" << cookie; + QTest::newRow("two-fields3") << "a=b; domain=.qt.nokia.com ; path=/ " << cookie; + QTest::newRow("two-fields4") << "a=b;path=/; domain=.qt.nokia.com" << cookie; + QTest::newRow("two-fields5") << "a=b; path=/ ; domain=.qt.nokia.com" << cookie; + QTest::newRow("two-fields6") << "a=b; path= / ; domain =.qt.nokia.com" << cookie; + + cookie.setSecure(true); + QTest::newRow("three-fields") << "a=b;domain=.qt.nokia.com;path=/;secure" << cookie; + QTest::newRow("three-fields2") << "a=b;secure;path=/;domain=.qt.nokia.com" << cookie; + QTest::newRow("three-fields3") << "a=b;secure;domain=.qt.nokia.com; path=/" << cookie; + QTest::newRow("three-fields4") << "a = b;secure;domain=.qt.nokia.com; path=/" << cookie; + + cookie = QNetworkCookie(); + cookie.setName("a"); + cookie.setValue("b"); + cookie.setExpirationDate(QDateTime(QDate(2012, 1, 29), QTime(23, 59, 59), Qt::UTC)); + QTest::newRow("broken-expiration1") << "a=b; expires=Sun, 29-Jan-2012 23:59:59;" << cookie; + + cookie.setExpirationDate(QDateTime(QDate(1999, 11, 9), QTime(23, 12, 40), Qt::UTC)); + QTest::newRow("expiration1") << "a=b;expires=Wednesday, 09-Nov-1999 23:12:40 GMT" << cookie; + QTest::newRow("expiration2") << "a=b;expires=Wed, 09-Nov-1999 23:12:40 GMT" << cookie; + QTest::newRow("expiration3") << "a=b; expires=Wednesday, 09-Nov-1999 23:12:40 GMT " << cookie; + QTest::newRow("expiration-utc") << "a=b;expires=Wednesday, 09-Nov-1999 23:12:40 UTC" << cookie; + + cookie.setExpirationDate(QDateTime(QDate(1989, 4, 14), QTime(3, 20, 0, 0), Qt::UTC)); + QTest::newRow("time-0") << "a=b;expires=14 Apr 89 03:20" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 4, 14), QTime(3, 20, 12, 0), Qt::UTC)); + QTest::newRow("time-1") << "a=b;expires=14 Apr 89 03:20:12" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 4, 14), QTime(3, 20, 12, 88), Qt::UTC)); + QTest::newRow("time-2") << "a=b;expires=14 Apr 89 03:20:12.88" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 4, 14), QTime(3, 20, 12, 88), Qt::UTC)); + QTest::newRow("time-3") << "a=b;expires=14 Apr 89 03:20:12.88am" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 4, 14), QTime(15, 20, 12, 88), Qt::UTC)); + QTest::newRow("time-4") << "a=b;expires=14 Apr 89 03:20:12.88pm" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 4, 14), QTime(3, 20, 12, 88), Qt::UTC)); + QTest::newRow("time-5") << "a=b;expires=14 Apr 89 03:20:12.88 Am" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 4, 14), QTime(15, 20, 12, 88), Qt::UTC)); + QTest::newRow("time-6") << "a=b;expires=14 Apr 89 03:20:12.88 PM" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 4, 14), QTime(15, 20, 12, 88), Qt::UTC)); + QTest::newRow("time-7") << "a=b;expires=14 Apr 89 3:20:12.88 PM" << cookie; + + // normal months + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(1, 1), Qt::UTC)); + QTest::newRow("months-1") << "a=b;expires=Jan 1 89 1:1" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 2, 1), QTime(1, 1), Qt::UTC)); + QTest::newRow("months-2") << "a=b;expires=Feb 1 89 1:1" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 3, 1), QTime(1, 1), Qt::UTC)); + QTest::newRow("months-3") << "a=b;expires=mar 1 89 1:1" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 4, 1), QTime(1, 1), Qt::UTC)); + QTest::newRow("months-4") << "a=b;expires=Apr 1 89 1:1" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 5, 1), QTime(1, 1), Qt::UTC)); + QTest::newRow("months-5") << "a=b;expires=May 1 89 1:1" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 6, 1), QTime(1, 1), Qt::UTC)); + QTest::newRow("months-6") << "a=b;expires=Jun 1 89 1:1" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 7, 1), QTime(1, 1), Qt::UTC)); + QTest::newRow("months-7") << "a=b;expires=Jul 1 89 1:1" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 8, 1), QTime(1, 1), Qt::UTC)); + QTest::newRow("months-8") << "a=b;expires=Aug 1 89 1:1" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 9, 1), QTime(1, 1), Qt::UTC)); + QTest::newRow("months-9") << "a=b;expires=Sep 1 89 1:1" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 10, 1), QTime(1, 1), Qt::UTC)); + QTest::newRow("months-10") << "a=b;expires=Oct 1 89 1:1" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 11, 1), QTime(1, 1), Qt::UTC)); + QTest::newRow("months-11") << "a=b;expires=Nov 1 89 1:1" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 12, 1), QTime(1, 1), Qt::UTC)); + QTest::newRow("months-12") << "a=b;expires=Dec 1 89 1:1" << cookie; + + // extra months + cookie.setExpirationDate(QDateTime(QDate(1989, 12, 1), QTime(1, 1), Qt::UTC)); + QTest::newRow("months-13") << "a=b;expires=December 1 89 1:1" << cookie; + QTest::newRow("months-14") << "a=b;expires=1 89 1:1 Dec" << cookie; + //cookie.setExpirationDate(QDateTime()); + //QTest::newRow("months-15") << "a=b;expires=1 89 1:1 De" << cookie; + cookie.setExpirationDate(QDateTime(QDate(2024, 2, 29), QTime(1, 1), Qt::UTC)); + QTest::newRow("months-16") << "a=b;expires=2024 29 Feb 1:1" << cookie; + cookie.setExpirationDate(QDateTime(QDate(2024, 2, 29), QTime(1, 1), Qt::UTC)); + QTest::newRow("months-17") << "a=b;expires=Fri, 29-Feb-2024 01:01:00 GMT" << cookie; + QTest::newRow("months-18") << "a=b;expires=2024 29 Feb 1:1 GMT" << cookie; + + // normal offsets + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC)); + QTest::newRow("zoneoffset-0") << "a=b;expires=Jan 1 89 8:0 PST" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC)); + QTest::newRow("zoneoffset-1") << "a=b;expires=Jan 1 89 8:0 PDT" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC)); + QTest::newRow("zoneoffset-2") << "a=b;expires=Jan 1 89 7:0 MST" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC)); + QTest::newRow("zoneoffset-3") << "a=b;expires=Jan 1 89 7:0 MDT" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC)); + QTest::newRow("zoneoffset-4") << "a=b;expires=Jan 1 89 6:0 CST" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC)); + QTest::newRow("zoneoffset-5") << "a=b;expires=Jan 1 89 6:0 CDT" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC)); + QTest::newRow("zoneoffset-6") << "a=b;expires=Jan 1 89 5:0 EST" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC)); + QTest::newRow("zoneoffset-7") << "a=b;expires=Jan 1 89 5:0 EDT" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC)); + QTest::newRow("zoneoffset-8") << "a=b;expires=Jan 1 89 4:0 AST" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC)); + QTest::newRow("zoneoffset-9") << "a=b;expires=Jan 1 89 3:0 NST" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC)); + QTest::newRow("zoneoffset-10") << "a=b;expires=Jan 1 89 0:0 GMT" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC)); + QTest::newRow("zoneoffset-11") << "a=b;expires=Jan 1 89 0:0 BST" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 2), QTime(0, 0), Qt::UTC)); + QTest::newRow("zoneoffset-12") << "a=b;expires=Jan 1 89 23:0 MET" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 2), QTime(0, 0), Qt::UTC)); + QTest::newRow("zoneoffset-13") << "a=b;expires=Jan 1 89 22:0 EET" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 2), QTime(0, 0), Qt::UTC)); + QTest::newRow("zoneoffset-14") << "a=b;expires=Jan 1 89 15:0 JST" << cookie; + + // extra offsets + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 2), QTime(0, 0), Qt::UTC)); + QTest::newRow("zoneoffset-14") << "a=b;expires=Jan 1 89 15:0 JST+1" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(1, 0), Qt::UTC)); + QTest::newRow("zoneoffset-15") << "a=b;expires=Jan 1 89 0:0 GMT+1" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC)); + QTest::newRow("zoneoffset-15b") << "a=b;expires=Jan 1 89 1:0 GMT-1" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(1, 0), Qt::UTC)); + QTest::newRow("zoneoffset-16") << "a=b;expires=Jan 1 89 0:0 GMT+01" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(1, 5), Qt::UTC)); + QTest::newRow("zoneoffset-17") << "a=b;expires=Jan 1 89 0:0 GMT+0105" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC)); + QTest::newRow("zoneoffset-18") << "a=b;expires=Jan 1 89 0:0 GMT+015" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC)); + QTest::newRow("zoneoffset-19") << "a=b;expires=Jan 1 89 0:0 GM" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC)); + QTest::newRow("zoneoffset-19b") << "a=b;expires=Jan 1 89 0:0 GMT" << cookie; + + // offsets from gmt + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(1, 0), Qt::UTC)); + QTest::newRow("zoneoffset-20") << "a=b;expires=Jan 1 89 0:0 +1" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(1, 0), Qt::UTC)); + QTest::newRow("zoneoffset-21") << "a=b;expires=Jan 1 89 0:0 +01" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(1, 1), Qt::UTC)); + QTest::newRow("zoneoffset-22") << "a=b;expires=Jan 1 89 0:0 +0101" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC)); + QTest::newRow("zoneoffset-23") << "a=b;expires=Jan 1 89 1:0 -1" << cookie; + + // Y2k + cookie.setExpirationDate(QDateTime(QDate(2000, 1, 1), QTime(0, 0), Qt::UTC)); + QTest::newRow("year-0") << "a=b;expires=Jan 1 00 0:0" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1970, 1, 1), QTime(0, 0), Qt::UTC)); + QTest::newRow("year-1") << "a=b;expires=Jan 1 70 0:0" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1971, 1, 1), QTime(0, 0), Qt::UTC)); + QTest::newRow("year-2") << "a=b;expires=Jan 1 71 0:0" << cookie; + + // Day, month, year + cookie.setExpirationDate(QDateTime(QDate(2013, 1, 2), QTime(0, 0), Qt::UTC)); + QTest::newRow("date-0") << "a=b;expires=Jan 2 13 0:0" << cookie; + QTest::newRow("date-1") << "a=b;expires=1-2-13 0:0" << cookie; + QTest::newRow("date-2") << "a=b;expires=1/2/13 0:0" << cookie; + QTest::newRow("date-3") << "a=b;expires=Jan 2 13 0:0" << cookie; + QTest::newRow("date-4") << "a=b;expires=Jan 2, 13 0:0" << cookie; + QTest::newRow("date-5") << "a=b;expires=1-2-13 0:0" << cookie; + QTest::newRow("date-6") << "a=b;expires=1/2/13 0:0" << cookie; + + // Known Year, determine month and day + cookie.setExpirationDate(QDateTime(QDate(1995, 1, 13), QTime(0, 0), Qt::UTC)); + QTest::newRow("knownyear-0") << "a=b;expires=13/1/95 0:0" << cookie; + QTest::newRow("knownyear-1") << "a=b;expires=95/13/1 0:0" << cookie; + QTest::newRow("knownyear-2") << "a=b;expires=1995/1/13 0:0" << cookie; + QTest::newRow("knownyear-3") << "a=b;expires=1995/13/1 0:0" << cookie; + cookie.setExpirationDate(QDateTime(QDate(1995, 1, 2), QTime(0, 0), Qt::UTC)); + QTest::newRow("knownyear-4") << "a=b;expires=1/2/95 0:0" << cookie; + QTest::newRow("knownyear-5") << "a=b;expires=95/1/2 0:0" << cookie; + + // Known Year, Known day, determining month + cookie.setExpirationDate(QDateTime(QDate(1995, 1, 13), QTime(0, 0), Qt::UTC)); + QTest::newRow("knownYD-0") << "a=b;expires=13/1/95 0:0" << cookie; + QTest::newRow("knownYD-1") << "a=b;expires=1/13/95 0:0" << cookie; + QTest::newRow("knownYD-2") << "a=b;expires=95/13/1 0:0" << cookie; + QTest::newRow("knownYD-3") << "a=b;expires=95/1/13 0:0" << cookie; + + // Month comes before Year + cookie.setExpirationDate(QDateTime(QDate(2021, 03, 26), QTime(0, 0), Qt::UTC)); + QTest::newRow("month-0") << "a=b;expires=26/03/21 0:0" << cookie; + cookie.setExpirationDate(QDateTime(QDate(2015, 12, 30), QTime(16, 25, 0, 0), Qt::UTC)); + QTest::newRow("month-1") << "a=b;expires=wed 16:25pm December 2015 30" << cookie; + cookie.setExpirationDate(QDateTime(QDate(2031, 11, 11), QTime(16, 25, 0, 0), Qt::UTC)); + QTest::newRow("month-2") << "a=b;expires=16:25 11 31 11" << cookie; + + // The very ambiguous cases + // Matching Firefox's behavior of guessing month, day, year in those cases + cookie.setExpirationDate(QDateTime(QDate(2013, 10, 2), QTime(0, 0), Qt::UTC)); + QTest::newRow("ambiguousd-0") << "a=b;expires=10/2/13 0:0" << cookie; + cookie.setExpirationDate(QDateTime(QDate(2013, 2, 10), QTime(0, 0), Qt::UTC)); + QTest::newRow("ambiguousd-1") << "a=b;expires=2/10/13 0:0" << cookie; + cookie.setExpirationDate(QDateTime(QDate(2010, 2, 3), QTime(0, 0), Qt::UTC)); + QTest::newRow("ambiguousd-2") << "a=b;expires=2/3/10 0:0" << cookie; + + // FYI If you try these in Firefox it wont set a cookie for the following two string + // because 03 is turned into the year at which point it is expired + cookie.setExpirationDate(QDateTime(QDate(2003, 2, 10), QTime(0, 0), Qt::UTC)); + QTest::newRow("ambiguousd-3") << "a=b;expires=2/10/3 0:0" << cookie; + cookie.setExpirationDate(QDateTime(QDate(2003, 10, 2), QTime(0, 0), Qt::UTC)); + QTest::newRow("ambiguousd-4") << "a=b;expires=10/2/3 0:0" << cookie; + + // These are the cookies that firefox's source says it can parse + cookie.setExpirationDate(QDateTime(QDate(1989, 4, 14), QTime(3, 20, 0, 0), Qt::UTC)); + QTest::newRow("firefox-0") << "a=b;expires=14 Apr 89 03:20" << cookie; + + cookie.setExpirationDate(QDateTime(QDate(1989, 4, 14), QTime(3, 20, 0, 0), Qt::UTC)); + QTest::newRow("firefox-1") << "a=b;expires=14 Apr 89 03:20 GMT" << cookie; + + cookie.setExpirationDate(QDateTime(QDate(1989, 3, 17), QTime(4, 1, 33, 0), Qt::UTC)); + QTest::newRow("firefox-2") << "a=b;expires=Fri, 17 Mar 89 4:01:33" << cookie; + + cookie.setExpirationDate(QDateTime(QDate(1989, 3, 17), QTime(4, 1, 0, 0), Qt::UTC)); + QTest::newRow("firefox-3") << "a=b;expires=Fri, 17 Mar 89 4:01 GMT" << cookie; + + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 16), QTime(16-8, 12, 0, 0), Qt::UTC)); + QTest::newRow("firefox-4") << "a=b;expires=Mon Jan 16 16:12 PDT 1989" << cookie; + + cookie.setExpirationDate(QDateTime(QDate(1989, 1, 16), QTime(17, 42, 0, 0), Qt::UTC)); + QTest::newRow("firefox-5") << "a=b;expires=Mon Jan 16 16:12 +0130 1989" << cookie; + + cookie.setExpirationDate(QDateTime(QDate(1992, 5, 6), QTime(16-9, 41, 0, 0), Qt::UTC)); + QTest::newRow("firefox-6") << "a=b;expires=6 May 1992 16:41-JST (Wednesday)" << cookie; + + cookie.setExpirationDate(QDateTime(QDate(1993, 8, 22), QTime(10, 59, 12, 82), Qt::UTC)); + QTest::newRow("firefox-7") << "a=b;expires=22-AUG-1993 10:59:12.82" << cookie; + + cookie.setExpirationDate(QDateTime(QDate(1993, 8, 22), QTime(22, 59, 0, 0), Qt::UTC)); + QTest::newRow("firefox-8") << "a=b;expires=22-AUG-1993 10:59pm" << cookie; + + cookie.setExpirationDate(QDateTime(QDate(1993, 8, 22), QTime(12, 59, 0, 0), Qt::UTC)); + QTest::newRow("firefox-9") << "a=b;expires=22-AUG-1993 12:59am" << cookie; + + cookie.setExpirationDate(QDateTime(QDate(1993, 8, 22), QTime(12, 59, 0, 0), Qt::UTC)); + QTest::newRow("firefox-10") << "a=b;expires=22-AUG-1993 12:59 PM" << cookie; + + cookie.setExpirationDate(QDateTime(QDate(1995, 8, 4), QTime(15, 54, 0, 0), Qt::UTC)); + QTest::newRow("firefox-11") << "a=b;expires=Friday, August 04, 1995 3:54 PM" << cookie; + + cookie.setExpirationDate(QDateTime(QDate(1995, 6, 21), QTime(16, 24, 34, 0), Qt::UTC)); + QTest::newRow("firefox-12") << "a=b;expires=06/21/95 04:24:34 PM" << cookie; + + cookie.setExpirationDate(QDateTime(QDate(1995, 6, 20), QTime(21, 7, 0, 0), Qt::UTC)); + QTest::newRow("firefox-13") << "a=b;expires=20/06/95 21:07" << cookie; + + cookie.setExpirationDate(QDateTime(QDate(1995, 6, 8), QTime(19-5, 32, 48, 0), Qt::UTC)); + QTest::newRow("firefox-14") << "a=b;expires=95-06-08 19:32:48 EDT" << cookie; + + // Edge cases caught by fuzzing + // These are about the default cause creates dates that don't exits + cookie.setExpirationDate(QDateTime(QDate(2030, 2, 25), QTime(1, 1, 0, 0), Qt::UTC)); + QTest::newRow("fuzz-0") << "a=b; expires=30 -000002 1:1 25;" << cookie; + + cookie.setExpirationDate(QDateTime(QDate(2031, 11, 20), QTime(1, 1, 0, 0), Qt::UTC)); + QTest::newRow("fuzz-1") << "a=b; expires=31 11 20 1:1;" << cookie; + + // April only has 30 days + cookie.setExpirationDate(QDateTime(QDate(2031, 4, 30), QTime(1, 1, 0, 0), Qt::UTC)); + QTest::newRow("fuzz-2") << "a=b; expires=31 30 4 1:1" << cookie; + + // 9 must be the month so 31 can't be the day + cookie.setExpirationDate(QDateTime(QDate(2031, 9, 21), QTime(1, 1, 0, 0), Qt::UTC)); + QTest::newRow("fuzz-3") << "a=b; expires=31 21 9 1:1" << cookie; + + // Year is known, then fallback to defaults of filling in month and day + cookie.setExpirationDate(QDateTime(QDate(2031, 11, 1), QTime(1, 1, 0, 0), Qt::UTC)); + QTest::newRow("fuzz-4") << "a=b; expires=31 11 01 1:1" << cookie; + + // 2 must be the month so 30 can't be the day + cookie.setExpirationDate(QDateTime(QDate(2030, 2, 20), QTime(1, 1, 0, 0), Qt::UTC)); + QTest::newRow("fuzz-5") << "a=b; expires=30 02 20 1:1" << cookie; + + cookie.setExpirationDate(QDateTime(QDate(2021, 12, 22), QTime(1, 1, 0, 0), Qt::UTC)); + QTest::newRow("fuzz-6") << "a=b; expires=2021 12 22 1:1" << cookie; + + cookie.setExpirationDate(QDateTime(QDate(2029, 2, 23), QTime(1, 1, 0, 0), Qt::UTC)); + QTest::newRow("fuzz-7") << "a=b; expires=29 23 Feb 1:1" << cookie; + + // 11 and 6 don't have 31 days + cookie.setExpirationDate(QDateTime(QDate(2031, 11, 06), QTime(1, 1, 0, 0), Qt::UTC)); + QTest::newRow("fuzz-8") << "a=b; expires=31 11 06 1:1" << cookie; + + // two-digit years: + // from 70 until 99, we assume 20th century + cookie.setExpirationDate(QDateTime(QDate(1999, 11, 9), QTime(23, 12, 40), Qt::UTC)); + QTest::newRow("expiration-2digit1") << "a=b; expires=Wednesday, 09-Nov-99 23:12:40 GMT " << cookie; + cookie.setExpirationDate(QDateTime(QDate(1970, 1, 1), QTime(23, 12, 40), Qt::UTC)); + QTest::newRow("expiration-2digit2") << "a=b; expires=Thursday, 01-Jan-70 23:12:40 GMT " << cookie; + // from 00 until 69, we assume 21st century + cookie.setExpirationDate(QDateTime(QDate(2000, 1, 1), QTime(23, 12, 40), Qt::UTC)); + QTest::newRow("expiration-2digit3") << "a=b; expires=Saturday, 01-Jan-00 23:12:40 GMT " << cookie; + cookie.setExpirationDate(QDateTime(QDate(2020, 1, 1), QTime(23, 12, 40), Qt::UTC)); + QTest::newRow("expiration-2digit4") << "a=b; expires=Wednesday, 01-Jan-20 23:12:40 GMT " << cookie; + cookie.setExpirationDate(QDateTime(QDate(2069, 1, 1), QTime(23, 12, 40), Qt::UTC)); + QTest::newRow("expiration-2digit5") << "a=b; expires=Wednesday, 01-Jan-69 23:12:40 GMT " << cookie; + + cookie.setExpirationDate(QDateTime(QDate(1999, 11, 9), QTime(23, 12, 40), Qt::UTC)); + + cookie.setPath("/"); + QTest::newRow("expires+path") << "a=b; expires=Wed, 09-Nov-1999 23:12:40 GMT; path=/" << cookie; + QTest::newRow("path+expires") << "a=b; path=/;expires=Wed, 09-Nov-1999 23:12:40 GMT " << cookie; + + cookie.setDomain(".qt.nokia.com"); + QTest::newRow("full") << "a=b; domain=.qt.nokia.com;expires=Wed, 09-Nov-1999 23:12:40 GMT;path=/" << cookie; + QTest::newRow("full2") << "a=b;path=/; expires=Wed, 09-Nov-1999 23:12:40 GMT ;domain=.qt.nokia.com" << cookie; + + // cookies obtained from the network: + cookie = QNetworkCookie("__siteid", "1"); + cookie.setPath("/"); + cookie.setExpirationDate(QDateTime(QDate(9999, 12, 31), QTime(23, 59, 59), Qt::UTC)); + QTest::newRow("network2") << "__siteid=1; expires=Fri, 31-Dec-9999 23:59:59 GMT; path=/" << cookie; + + cookie = QNetworkCookie("YM.LC", "v=2&m=9993_262838_159_1558_1063_0_5649_4012_3776161073,9426_260205_549_1295_1336_0_5141_4738_3922731647,6733_258196_952_1364_643_0_3560_-1_0,3677_237633_1294_1294_19267_0_3244_29483_4102206176,1315_235149_1693_1541_941_0_3224_1691_1861378060,1858_214311_2100_1298_19538_0_2873_30900_716411652,6258_212007_2506_1285_1017_0_2868_3606_4288540264,3743_207884_2895_1362_2759_0_2545_7114_3388520216,2654_205253_3257_1297_1332_0_2504_4682_3048534803,1891_184881_3660_1291_19079_0_978_29178_2592538685&f=1&n=20&s=date&o=down&e=1196548712&b=Inbox&u=removed"); + cookie.setPath("/"); + cookie.setDomain("mail.yahoo.com"); + QTest::newRow("network3") << "YM.LC=v=2&m=9993_262838_159_1558_1063_0_5649_4012_3776161073,9426_260205_549_1295_1336_0_5141_4738_3922731647,6733_258196_952_1364_643_0_3560_-1_0,3677_237633_1294_1294_19267_0_3244_29483_4102206176,1315_235149_1693_1541_941_0_3224_1691_1861378060,1858_214311_2100_1298_19538_0_2873_30900_716411652,6258_212007_2506_1285_1017_0_2868_3606_4288540264,3743_207884_2895_1362_2759_0_2545_7114_3388520216,2654_205253_3257_1297_1332_0_2504_4682_3048534803,1891_184881_3660_1291_19079_0_978_29178_2592538685&f=1&n=20&s=date&o=down&e=1196548712&b=Inbox&u=removed; path=/; domain=mail.yahoo.com" << cookie; + + cookie = QNetworkCookie("__ac", "\"c2hhdXNtYW46U2FTYW80Wm8%3D\""); + cookie.setPath("/"); + cookie.setExpirationDate(QDateTime(QDate(2008, 8, 30), QTime(20, 21, 49), Qt::UTC)); + QTest::newRow("network4") << "__ac=\"c2hhdXNtYW46U2FTYW80Wm8%3D\"; Path=/; Expires=Sat, 30 Aug 2008 20:21:49 +0000" << cookie; + + // linkedin.com sends cookies in quotes and expects the cookie in quotes + cookie = QNetworkCookie("leo_auth_token", "\"GST:UroVXaxYA3sVSkoVjMNH9bj4dZxVzK2yekgrAUxMfUsyLTNyPjoP60:1298974875:b675566ae32ab36d7a708c0efbf446a5c22b9fca\""); + cookie.setPath("/"); + cookie.setExpirationDate(QDateTime(QDate(2011, 3, 1), QTime(10, 51, 14), Qt::UTC)); + QTest::newRow("network5") << "leo_auth_token=\"GST:UroVXaxYA3sVSkoVjMNH9bj4dZxVzK2yekgrAUxMfUsyLTNyPjoP60:1298974875:b675566ae32ab36d7a708c0efbf446a5c22b9fca\"; Version=1; Max-Age=1799; Expires=Tue, 01-Mar-2011 10:51:14 GMT; Path=/" << cookie; + + +} + +void tst_QNetworkCookie::parseSingleCookie() +{ + QFETCH(QString, cookieString); + QFETCH(QNetworkCookie, expectedCookie); + + QList result = QNetworkCookie::parseCookies(cookieString.toLatin1()); + + //QEXPECT_FAIL("network2", "QDateTime parsing problem: the date is beyond year 8000", Abort); + QCOMPARE(result.count(), 1); + QEXPECT_FAIL("network3", "Cookie value contains commas, violating the HTTP spec", Abort); + QCOMPARE(result.at(0), expectedCookie); + + result = QNetworkCookie::parseCookies(result.at(0).toRawForm()); + QCOMPARE(result.count(), 1); + + // Drop any millisecond information, if there's any + QDateTime dt = expectedCookie.expirationDate(); + if (dt.isValid()) { + QTime t = dt.time(); + dt.setTime(t.addMSecs(-t.msec())); + expectedCookie.setExpirationDate(dt); + } + + QCOMPARE(result.at(0), expectedCookie); +} + +void tst_QNetworkCookie::parseMultipleCookies_data() +{ + QTest::addColumn("cookieString"); + QTest::addColumn >("expectedCookies"); + + QList list; + QTest::newRow("empty") << "" << list; + + // these are technically empty cookies: + QTest::newRow("invalid-01") << ";" << list; + QTest::newRow("invalid-02") << " " << list; + QTest::newRow("invalid-03") << " ," << list; + QTest::newRow("invalid-04") << ";;,, ; ; , , ; , ;" << list; + + // these are really invalid: + // reason: malformed NAME=VALUE pair + QTest::newRow("invalid-05") << "foo" << list; + QTest::newRow("invalid-06") << "=b" << list; + QTest::newRow("invalid-09") << "foo,a=b" << list; + QTest::newRow("invalid-11") << ";path=/" << list; + + // reason: malformed expiration date string + QTest::newRow("invalid-12") << "a=b;expires=" << list; + QTest::newRow("invalid-13") << "a=b;expires=foobar" << list; + QTest::newRow("invalid-14") << "a=b;expires=foobar, abc" << list; + QTest::newRow("invalid-15") << "a=b;expires=foobar, dd-mmm-yyyy hh:mm:ss GMT; path=/" << list; + QTest::newRow("invalid-16") << "a=b;expires=foobar, 32-Caz-1999 24:01:60 GMT; path=/" << list; + + QNetworkCookie cookie; + cookie.setName("a"); + list += cookie; + cookie.setName("c"); + cookie.setValue("d"); + list += cookie; + QTest::newRow("two-1") << "a=,c=d" << list; + QTest::newRow("two-2") << "a=, c=d" << list; + QTest::newRow("two-3") << "a= ,c=d" << list; + QTest::newRow("two-4") << "a= , c=d" << list; + + list.clear(); + list += cookie; + cookie.setName("a"); + cookie.setValue(QByteArray()); + list += cookie; + QTest::newRow("two-5") << "c=d,a=" << list; + QTest::newRow("two-6") << "c=d, a=" << list; + QTest::newRow("two-7") << "c=d , a=" << list; + + cookie.setName("foo"); + cookie.setValue("bar"); + cookie.setPath("/"); + list += cookie; + QTest::newRow("complex-1") << "c=d, a=, foo=bar; path=/" << list; + + cookie.setName("baz"); + cookie.setDomain(".qt.nokia.com"); + list.prepend(cookie); + QTest::newRow("complex-2") << "baz=bar; path=/; domain=.qt.nokia.com, c=d,a=,foo=bar; path=/" << list; + + // cookies obtained from the network: + cookie = QNetworkCookie("id", "51706646077999719"); + cookie.setDomain(".bluestreak.com"); + cookie.setPath("/"); + cookie.setExpirationDate(QDateTime(QDate(2017, 12, 05), QTime(9, 11, 7), Qt::UTC)); + list << cookie; + cookie.setName("bb"); + cookie.setValue("\\\"K14144t\\\"_AAQ\\\"ototrK_A_ttot44AQ4KwoRQtoto|"); + list << cookie; + cookie.setName("adv"); + cookie.setValue(QByteArray()); + list << cookie; + QTest::newRow("network1") << "id=51706646077999719 bb=\"K14144t\"_AAQ\"ototrK_A_ttot44AQ4KwoRQtoto| adv=; Domain=.bluestreak.com; expires=Tuesday 05-Dec-2017 09:11:07 GMT; path=/;" << list; + + QNetworkCookie cookieA; + cookieA.setName("a"); + cookieA.setValue("b"); + + QNetworkCookie cookieB; + cookieB.setName("c"); + cookieB.setValue("d"); + + // NewLine + cookieA.setExpirationDate(QDateTime(QDate(2009, 3, 10), QTime(7, 0, 0, 0), Qt::UTC)); + cookieB.setExpirationDate(QDateTime(QDate(2009, 3, 20), QTime(7, 0, 0, 0), Qt::UTC)); + list = QList() << cookieA << cookieB; + QTest::newRow("real-0") << "a=b; expires=Tue Mar 10 07:00:00 2009 GMT\nc=d; expires=Fri Mar 20 07:00:00 2009 GMT" << list; + QTest::newRow("real-1") << "a=b; expires=Tue Mar 10 07:00:00 2009 GMT\n\nc=d; expires=Fri Mar 20 07:00:00 2009 GMT" << list; + QTest::newRow("real-2") << "a=b; expires=Mar 10 07:00:00 2009 GMT, Tue\nc=d; expires=Fri Mar 20 07:00:00 2009 GMT" << list; + + // Match firefox's behavior + cookieA.setPath("/foo"); + list = QList() << cookieA << cookieB; + QTest::newRow("real-3") << "a=b; expires=Mar 10 07:00:00 2009 GMT, Tue; path=/foo\nc=d; expires=Fri Mar 20 07:00:00 2009 GMT" << list; + + // do not accept cookies with non-alphanumeric characters in domain field (QTBUG-11029) + cookie = QNetworkCookie("NonAlphNumDomName", "NonAlphNumDomValue"); + cookie.setDomain("!@#$%^&*();:."); // the ';' is actually problematic, because it is a separator + list = QList(); + QTest::newRow("domain-non-alpha-numeric") << "NonAlphNumDomName=NonAlphNumDomValue; domain=!@#$%^&*()" << list; + QTest::newRow("expiration-3digit1") << "a=b; expires=123" << list; // used to ASSERT +} + +void tst_QNetworkCookie::parseMultipleCookies() +{ + QFETCH(QString, cookieString); + QFETCH(QList, expectedCookies); + + QList result = QNetworkCookie::parseCookies(cookieString.toLatin1()); + + QEXPECT_FAIL("network1", "Apparently multiple cookies set in one request (and an invalid date)", Abort); + QCOMPARE(result, expectedCookies); +} + +QTEST_MAIN(tst_QNetworkCookie) +#include "tst_qnetworkcookie.moc" diff --git a/tests/auto/network/access/qnetworkcookiejar/.gitignore b/tests/auto/network/access/qnetworkcookiejar/.gitignore new file mode 100644 index 0000000000..918754a274 --- /dev/null +++ b/tests/auto/network/access/qnetworkcookiejar/.gitignore @@ -0,0 +1 @@ +tst_qnetworkcookiejar diff --git a/tests/auto/network/access/qnetworkcookiejar/qnetworkcookiejar.pro b/tests/auto/network/access/qnetworkcookiejar/qnetworkcookiejar.pro new file mode 100644 index 0000000000..4e5f01745a --- /dev/null +++ b/tests/auto/network/access/qnetworkcookiejar/qnetworkcookiejar.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +SOURCES += tst_qnetworkcookiejar.cpp + +QT = core core-private network network-private +symbian: TARGET.CAPABILITY = NetworkServices diff --git a/tests/auto/network/access/qnetworkcookiejar/tst_qnetworkcookiejar.cpp b/tests/auto/network/access/qnetworkcookiejar/tst_qnetworkcookiejar.cpp new file mode 100644 index 0000000000..1b4256bb00 --- /dev/null +++ b/tests/auto/network/access/qnetworkcookiejar/tst_qnetworkcookiejar.cpp @@ -0,0 +1,446 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include +#include "private/qtldurl_p.h" + +class tst_QNetworkCookieJar: public QObject +{ + Q_OBJECT + +private slots: + void getterSetter(); + void setCookiesFromUrl_data(); + void setCookiesFromUrl(); + void cookiesForUrl_data(); + void cookiesForUrl(); + void effectiveTLDs_data(); + void effectiveTLDs(); +}; + +QT_BEGIN_NAMESPACE + +namespace QTest { + template<> + char *toString(const QNetworkCookie &cookie) + { + return qstrdup(cookie.toRawForm()); + } + + template<> + char *toString(const QList &list) + { + QString result = "QList("; + bool first = true; + foreach (QNetworkCookie cookie, list) { + if (!first) + result += ", "; + first = false; + result += QString::fromLatin1("QNetworkCookie(%1)").arg(QLatin1String(cookie.toRawForm())); + } + + return qstrdup(result.append(')').toLocal8Bit()); + } +} + +QT_END_NAMESPACE + +class MyCookieJar: public QNetworkCookieJar +{ +public: + inline QList allCookies() const + { return QNetworkCookieJar::allCookies(); } + inline void setAllCookies(const QList &cookieList) + { QNetworkCookieJar::setAllCookies(cookieList); } +}; + +void tst_QNetworkCookieJar::getterSetter() +{ + MyCookieJar jar; + + QVERIFY(jar.allCookies().isEmpty()); + + QList list; + QNetworkCookie cookie; + cookie.setName("a"); + list << cookie; + + jar.setAllCookies(list); + QCOMPARE(jar.allCookies(), list); +} + +void tst_QNetworkCookieJar::setCookiesFromUrl_data() +{ + QTest::addColumn >("preset"); + QTest::addColumn("newCookie"); + QTest::addColumn("referenceUrl"); + QTest::addColumn >("expectedResult"); + QTest::addColumn("setCookies"); + + QList preset; + QList result; + QNetworkCookie cookie; + + cookie.setName("a"); + cookie.setPath("/"); + cookie.setDomain(".foo.tld"); + result += cookie; + QTest::newRow("just-add") << preset << cookie << "http://www.foo.tld" << result << true; + + preset = result; + QTest::newRow("replace-1") << preset << cookie << "http://www.foo.tld" << result << true; + + cookie.setValue("bc"); + result.clear(); + result += cookie; + QTest::newRow("replace-2") << preset << cookie << "http://www.foo.tld" << result << true; + + preset = result; + cookie.setName("d"); + result += cookie; + QTest::newRow("append") << preset << cookie << "http://www.foo.tld" << result << true; + + cookie = preset.at(0); + result = preset; + cookie.setPath("/something"); + result += cookie; + QTest::newRow("diff-path") << preset << cookie << "http://www.foo.tld/something" << result << true; + + preset.clear(); + preset += cookie; + cookie.setPath("/"); + QTest::newRow("diff-path-order") << preset << cookie << "http://www.foo.tld" << result << true; + + preset.clear(); + result.clear(); + QNetworkCookie finalCookie = cookie; + cookie.setDomain("foo.tld"); + finalCookie.setDomain(".foo.tld"); + result += finalCookie; + QTest::newRow("should-add-dot-prefix") << preset << cookie << "http://www.foo.tld" << result << true; + + result.clear(); + cookie.setDomain(""); + finalCookie.setDomain("www.foo.tld"); + result += finalCookie; + QTest::newRow("should-set-default-domain") << preset << cookie << "http://www.foo.tld" << result << true; + + // security test: + result.clear(); + preset.clear(); + cookie.setDomain("something.completely.different"); + QTest::newRow("security-domain-1") << preset << cookie << "http://www.foo.tld" << result << false; + + // we want the cookie to be accepted although the path does not match, see QTBUG-5815 + cookie.setDomain(".foo.tld"); + cookie.setPath("/something"); + result += cookie; + QTest::newRow("security-path-1") << preset << cookie << "http://www.foo.tld" << result << true; + + // check effective TLDs + // 1. co.uk is an effective TLD, should be denied + result.clear(); + preset.clear(); + cookie.setPath("/"); + cookie.setDomain(".co.uk"); + QTest::newRow("effective-tld1-denied") << preset << cookie << "http://something.co.uk" << result << false; + cookie.setDomain("co.uk"); + QTest::newRow("effective-tld1-denied2") << preset << cookie << "http://something.co.uk" << result << false; + cookie.setDomain(".something.co.uk"); + result += cookie; + QTest::newRow("effective-tld1-accepted") << preset << cookie << "http://something.co.uk" << result << true; + + // 2. anything .ar is an effective TLD ('*.ar'), but 'gobiernoelectronico.ar' is an exception + result.clear(); + preset.clear(); + cookie.setDomain(".farmacia.ar"); + QTest::newRow("effective-tld2-denied") << preset << cookie << "http://farmacia.ar" << result << false; + QTest::newRow("effective-tld2-denied2") << preset << cookie << "http://www.farmacia.ar" << result << false; + QTest::newRow("effective-tld2-denied3") << preset << cookie << "http://www.anything.farmacia.ar" << result << false; + cookie.setDomain(".gobiernoelectronico.ar"); + result += cookie; + QTest::newRow("effective-tld2-accepted") << preset << cookie << "http://www.gobiernoelectronico.ar" << result << true; + + + // setting the defaults: + finalCookie = cookie; + finalCookie.setPath("/something/"); + finalCookie.setDomain("www.foo.tld"); + cookie.setPath(""); + cookie.setDomain(""); + result.clear(); + result += finalCookie; + QTest::newRow("defaults-1") << preset << cookie << "http://www.foo.tld/something/" << result << true; + + finalCookie.setPath("/"); + result.clear(); + result += finalCookie; + QTest::newRow("defaults-2") << preset << cookie << "http://www.foo.tld" << result << true; + + // security test: do not accept cookie domains like ".com" nor ".com." (see RFC 2109 section 4.3.2) + result.clear(); + preset.clear(); + cookie.setDomain(".com"); + QTest::newRow("rfc2109-4.3.2-ex3") << preset << cookie << "http://x.foo.com" << result << false; + + result.clear(); + preset.clear(); + cookie.setDomain(".com."); + QTest::newRow("rfc2109-4.3.2-ex3-2") << preset << cookie << "http://x.foo.com" << result << false; +} + +void tst_QNetworkCookieJar::setCookiesFromUrl() +{ + QFETCH(QList, preset); + QFETCH(QNetworkCookie, newCookie); + QFETCH(QString, referenceUrl); + QFETCH(QList, expectedResult); + QFETCH(bool, setCookies); + + QList cookieList; + cookieList += newCookie; + MyCookieJar jar; + jar.setAllCookies(preset); + QCOMPARE(jar.setCookiesFromUrl(cookieList, referenceUrl), setCookies); + + QList result = jar.allCookies(); + foreach (QNetworkCookie cookie, expectedResult) { + QVERIFY2(result.contains(cookie), cookie.toRawForm()); + result.removeAll(cookie); + } + QVERIFY2(result.isEmpty(), QTest::toString(result)); +} + +void tst_QNetworkCookieJar::cookiesForUrl_data() +{ + QTest::addColumn >("allCookies"); + QTest::addColumn("url"); + QTest::addColumn >("expectedResult"); + + QList allCookies; + QList result; + + QTest::newRow("no-cookies") << allCookies << "http://foo.bar/" << result; + + QNetworkCookie cookie; + cookie.setName("a"); + cookie.setPath("/web"); + cookie.setDomain(".nokia.com"); + allCookies += cookie; + + QTest::newRow("no-match-1") << allCookies << "http://foo.bar/" << result; + QTest::newRow("no-match-2") << allCookies << "http://foo.bar/web" << result; + QTest::newRow("no-match-3") << allCookies << "http://foo.bar/web/wiki" << result; + QTest::newRow("no-match-4") << allCookies << "http://nokia.com" << result; + QTest::newRow("no-match-5") << allCookies << "http://qt.nokia.com" << result; + QTest::newRow("no-match-6") << allCookies << "http://nokia.com/webinar" << result; + QTest::newRow("no-match-7") << allCookies << "http://qt.nokia.com/webinar" << result; + + result = allCookies; + QTest::newRow("match-1") << allCookies << "http://nokia.com/web" << result; + QTest::newRow("match-2") << allCookies << "http://nokia.com/web/" << result; + QTest::newRow("match-3") << allCookies << "http://nokia.com/web/content" << result; + QTest::newRow("match-4") << allCookies << "http://qt.nokia.com/web" << result; + QTest::newRow("match-4") << allCookies << "http://qt.nokia.com/web/" << result; + QTest::newRow("match-6") << allCookies << "http://qt.nokia.com/web/content" << result; + + cookie.setPath("/web/wiki"); + allCookies += cookie; + + // exact same results as before: + QTest::newRow("one-match-1") << allCookies << "http://nokia.com/web" << result; + QTest::newRow("one-match-2") << allCookies << "http://nokia.com/web/" << result; + QTest::newRow("one-match-3") << allCookies << "http://nokia.com/web/content" << result; + QTest::newRow("one-match-4") << allCookies << "http://qt.nokia.com/web" << result; + QTest::newRow("one-match-4") << allCookies << "http://qt.nokia.com/web/" << result; + QTest::newRow("one-match-6") << allCookies << "http://qt.nokia.com/web/content" << result; + + result.prepend(cookie); // longer path, it must match first + QTest::newRow("two-matches-1") << allCookies << "http://nokia.com/web/wiki" << result; + QTest::newRow("two-matches-2") << allCookies << "http://qt.nokia.com/web/wiki" << result; + + // invert the order; + allCookies.clear(); + allCookies << result.at(1) << result.at(0); + QTest::newRow("two-matches-3") << allCookies << "http://nokia.com/web/wiki" << result; + QTest::newRow("two-matches-4") << allCookies << "http://qt.nokia.com/web/wiki" << result; + + // expired cookie + allCookies.clear(); + cookie.setExpirationDate(QDateTime::fromString("09-Nov-1999", "dd-MMM-yyyy")); + allCookies += cookie; + result.clear(); + QTest::newRow("exp-match-1") << allCookies << "http://nokia.com/web" << result; + QTest::newRow("exp-match-2") << allCookies << "http://nokia.com/web/" << result; + QTest::newRow("exp-match-3") << allCookies << "http://nokia.com/web/content" << result; + QTest::newRow("exp-match-4") << allCookies << "http://qt.nokia.com/web" << result; + QTest::newRow("exp-match-4") << allCookies << "http://qt.nokia.com/web/" << result; + QTest::newRow("exp-match-6") << allCookies << "http://qt.nokia.com/web/content" << result; + + // path matching + allCookies.clear(); + QNetworkCookie anotherCookie; + anotherCookie.setName("a"); + anotherCookie.setPath("/web"); + anotherCookie.setDomain(".nokia.com"); + allCookies += anotherCookie; + result.clear(); + QTest::newRow("path-unmatch-1") << allCookies << "http://nokia.com/" << result; + QTest::newRow("path-unmatch-2") << allCookies << "http://nokia.com/something/else" << result; + result += anotherCookie; + QTest::newRow("path-match-1") << allCookies << "http://nokia.com/web" << result; + QTest::newRow("path-match-2") << allCookies << "http://nokia.com/web/" << result; + QTest::newRow("path-match-3") << allCookies << "http://nokia.com/web/content" << result; + + // secure cookies + allCookies.clear(); + result.clear(); + QNetworkCookie secureCookie; + secureCookie.setName("a"); + secureCookie.setPath("/web"); + secureCookie.setDomain(".nokia.com"); + secureCookie.setSecure(true); + allCookies += secureCookie; + QTest::newRow("no-match-secure-1") << allCookies << "http://nokia.com/web" << result; + QTest::newRow("no-match-secure-2") << allCookies << "http://qt.nokia.com/web" << result; + result += secureCookie; + QTest::newRow("match-secure-1") << allCookies << "https://nokia.com/web" << result; + QTest::newRow("match-secure-2") << allCookies << "https://qt.nokia.com/web" << result; + +} + +void tst_QNetworkCookieJar::cookiesForUrl() +{ + QFETCH(QList, allCookies); + QFETCH(QString, url); + QFETCH(QList, expectedResult); + + MyCookieJar jar; + jar.setAllCookies(allCookies); + + QList result = jar.cookiesForUrl(url); + QCOMPARE(result, expectedResult); +} + +void tst_QNetworkCookieJar::effectiveTLDs_data() +{ + QTest::addColumn("domain"); + QTest::addColumn("isTLD"); + + QTest::newRow("yes1") << "com" << true; + QTest::newRow("yes2") << "de" << true; + QTest::newRow("yes3") << "ulm.museum" << true; + QTest::newRow("yes4") << "krodsherad.no" << true; + QTest::newRow("yes5") << "1.bg" << true; + QTest::newRow("yes6") << "com.cn" << true; + QTest::newRow("yes7") << "org.ws" << true; + QTest::newRow("yes8") << "co.uk" << true; + QTest::newRow("yes9") << "wallonie.museum" << true; + + QTest::newRow("no1") << "anything.com" << false; + QTest::newRow("no2") << "anything.de" << false; + QTest::newRow("no3") << "eselsberg.ulm.museum" << false; + QTest::newRow("no4") << "noe.krodsherad.no" << false; + QTest::newRow("no5") << "2.1.bg" << false; + QTest::newRow("no6") << "foo.com.cn" << false; + QTest::newRow("no7") << "something.org.ws" << false; + QTest::newRow("no8") << "teatime.co.uk" << false; + QTest::newRow("no9") << "bla" << false; + QTest::newRow("no10") << "bla.bla" << false; + + const ushort s1[] = {0x74, 0x72, 0x61, 0x6e, 0xf8, 0x79, 0x2e, 0x6e, 0x6f, 0x00}; // xn--trany-yua.no + const ushort s2[] = {0x5d9, 0x5e8, 0x5d5, 0x5e9, 0x5dc, 0x5d9, 0x5dd, 0x2e, 0x6d, 0x75, 0x73, 0x65, 0x75, 0x6d, 0x00}; // xn--9dbhblg6di.museum + const ushort s3[] = {0x7ec4, 0x7e54, 0x2e, 0x68, 0x6b, 0x00}; // xn--mk0axi.hk + const ushort s4[] = {0x7f51, 0x7edc, 0x2e, 0x63, 0x6e, 0x00}; // xn--io0a7i.cn + const ushort s5[] = {0x72, 0xe1, 0x68, 0x6b, 0x6b, 0x65, 0x72, 0xe1, 0x76, 0x6a, 0x75, 0x2e, 0x6e, 0x6f, 0x00}; // xn--rhkkervju-01af.no + const ushort s6[] = {0xb9a, 0xbbf, 0xb99, 0xbcd, 0xb95, 0xbaa, 0xbcd, 0xbaa, 0xbc2, 0xbb0, 0xbcd, 0x00}; // xn--clchc0ea0b2g2a9gcd + const ushort s7[] = {0x627, 0x644, 0x627, 0x631, 0x62f, 0x646, 0x00}; // xn--mgbayh7gpa + const ushort s8[] = {0x63, 0x6f, 0x72, 0x72, 0x65, 0x69, 0x6f, 0x73, 0x2d, 0x65, 0x2d, 0x74, 0x65, 0x6c, 0x65, + 0x63, 0x6f, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0xe7, 0xf5, 0x65, 0x73, 0x2e, 0x6d, 0x75, + 0x73, 0x65, 0x75, 0x6d, 0x00}; // xn--correios-e-telecomunicaes-ghc29a.museum + QTest::newRow("yes-specialchars1") << QString::fromUtf16(s1) << true; + QTest::newRow("yes-specialchars2") << QString::fromUtf16(s2) << true; + QTest::newRow("yes-specialchars3") << QString::fromUtf16(s3) << true; + QTest::newRow("yes-specialchars4") << QString::fromUtf16(s4) << true; + QTest::newRow("yes-specialchars5") << QString::fromUtf16(s5) << true; + QTest::newRow("yes-specialchars6") << QString::fromUtf16(s6) << true; + QTest::newRow("yes-specialchars7") << QString::fromUtf16(s7) << true; + QTest::newRow("yes-specialchars8") << QString::fromUtf16(s8) << true; + + QTest::newRow("no-specialchars1") << QString::fromUtf16(s1).prepend("something") << false; + QTest::newRow("no-specialchars2") << QString::fromUtf16(s2).prepend(QString::fromUtf16(s2)) << false; + QTest::newRow("no-specialchars2.5") << QString::fromUtf16(s2).prepend("whatever") << false; + QTest::newRow("no-specialchars3") << QString::fromUtf16(s3).prepend("foo") << false; + QTest::newRow("no-specialchars4") << QString::fromUtf16(s4).prepend("bar") << false; + QTest::newRow("no-specialchars5") << QString::fromUtf16(s5).prepend(QString::fromUtf16(s2)) << false; + QTest::newRow("no-specialchars6") << QString::fromUtf16(s6).prepend(QLatin1Char('.') + QString::fromUtf16(s6)) << false; + QTest::newRow("no-specialchars7") << QString::fromUtf16(s7).prepend("bla") << false; + QTest::newRow("no-specialchars8") << QString::fromUtf16(s8).append("foo") << false; + + QTest::newRow("exception1") << "pref.iwate.jp" << false; + QTest::newRow("exception2") << "omanpost.om" << false; + QTest::newRow("exception3") << "omantel.om" << false; + QTest::newRow("exception4") << "gobiernoelectronico.ar" << false; + QTest::newRow("exception5") << "pref.ishikawa.jp" << false; + + QTest::newRow("yes-wildcard1") << "*.jm" << true; + QTest::newRow("yes-wildcard1.5") << "anything.jm" << true; + QTest::newRow("yes-wildcard2") << "something.kh" << true; + QTest::newRow("yes-wildcard3") << "whatever.uk" << true; + QTest::newRow("yes-wildcard4") << "anything.shizuoka.jp" << true; + QTest::newRow("yes-wildcard5") << "foo.sch.uk" << true; +} + +void tst_QNetworkCookieJar::effectiveTLDs() +{ +#ifndef QT_BUILD_INTERNAL + QSKIP("Test requires private API", SkipAll); +#endif + QFETCH(QString, domain); + QFETCH(bool, isTLD); + QCOMPARE(qIsEffectiveTLD(domain), isTLD); +} + +QTEST_MAIN(tst_QNetworkCookieJar) +#include "tst_qnetworkcookiejar.moc" + diff --git a/tests/auto/network/access/qnetworkdiskcache/.gitignore b/tests/auto/network/access/qnetworkdiskcache/.gitignore new file mode 100644 index 0000000000..37a1ff2a42 --- /dev/null +++ b/tests/auto/network/access/qnetworkdiskcache/.gitignore @@ -0,0 +1 @@ +tst_qnetworkdiskcache diff --git a/tests/auto/network/access/qnetworkdiskcache/qnetworkdiskcache.pro b/tests/auto/network/access/qnetworkdiskcache/qnetworkdiskcache.pro new file mode 100644 index 0000000000..c05171dac7 --- /dev/null +++ b/tests/auto/network/access/qnetworkdiskcache/qnetworkdiskcache.pro @@ -0,0 +1,7 @@ +load(qttest_p4) +QT -= gui +QT += network +SOURCES += tst_qnetworkdiskcache.cpp + +symbian: TARGET.CAPABILITY = NetworkServices + diff --git a/tests/auto/network/access/qnetworkdiskcache/tst_qnetworkdiskcache.cpp b/tests/auto/network/access/qnetworkdiskcache/tst_qnetworkdiskcache.cpp new file mode 100644 index 0000000000..030eae60a0 --- /dev/null +++ b/tests/auto/network/access/qnetworkdiskcache/tst_qnetworkdiskcache.cpp @@ -0,0 +1,677 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include +#include +#include "../../../../shared/util.h" + +#define EXAMPLE_URL "http://user:pass@www.example.com/#foo" +//cached objects are organized into these many subdirs +#define NUM_SUBDIRECTORIES 16 + +class tst_QNetworkDiskCache : public QObject +{ + Q_OBJECT + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private slots: + void qnetworkdiskcache_data(); + void qnetworkdiskcache(); + + void prepare(); + void cacheSize(); + void clear(); + void data_data(); + void data(); + void metaData(); + void remove(); + void setCacheDirectory_data(); + void setCacheDirectory(); + void updateMetaData(); + void fileMetaData(); + void expire(); + + void oldCacheVersionFile_data(); + void oldCacheVersionFile(); + + void sync(); + + void crashWhenParentingCache(); +}; + +// FIXME same as in tst_qnetworkreply.cpp .. could be unified +// Does not work for POST/PUT! +class MiniHttpServer: public QTcpServer +{ + Q_OBJECT +public: + QTcpSocket *client; // always the last one that was received + QByteArray dataToTransmit; + QByteArray receivedData; + bool doClose; + bool multiple; + int totalConnections; + + MiniHttpServer(const QByteArray &data) : client(0), dataToTransmit(data), doClose(true), multiple(false), totalConnections(0) + { + listen(); + connect(this, SIGNAL(newConnection()), this, SLOT(doAccept())); + } + +public slots: + void doAccept() + { + client = nextPendingConnection(); + client->setParent(this); + ++totalConnections; + connect(client, SIGNAL(readyRead()), this, SLOT(readyReadSlot())); + } + + void readyReadSlot() + { + receivedData += client->readAll(); + int doubleEndlPos = receivedData.indexOf("\r\n\r\n"); + + if (doubleEndlPos != -1) { + // multiple requests incoming. remove the bytes of the current one + if (multiple) + receivedData.remove(0, doubleEndlPos+4); + + client->write(dataToTransmit); + if (doClose) { + client->disconnectFromHost(); + disconnect(client, 0, this, 0); + client = 0; + } + } + } +}; + +// Subclass that exposes the protected functions. +class SubQNetworkDiskCache : public QNetworkDiskCache +{ +public: + ~SubQNetworkDiskCache() + { + if (!cacheDirectory().isEmpty()) + clear(); + } + + QNetworkCacheMetaData call_fileMetaData(QString const &fileName) + { return SubQNetworkDiskCache::fileMetaData(fileName); } + + qint64 call_expire() + { return SubQNetworkDiskCache::expire(); } + + void setupWithOne(const QUrl &url, const QNetworkCacheMetaData &metaData = QNetworkCacheMetaData()) + { + setCacheDirectory(QDir::tempPath() + "/diskCache"); + + QIODevice *d = 0; + if (metaData.isValid()) { + d = prepare(metaData); + } else { + QNetworkCacheMetaData m; + m.setUrl(url); + QNetworkCacheMetaData::RawHeader header("content-type", "text/html"); + QNetworkCacheMetaData::RawHeaderList list; + list.append(header); + m.setRawHeaders(list); + d = prepare(m); + } + d->write("Hello World!"); + insert(d); + } +}; + +// This will be called before the first test function is executed. +// It is only called once. +void tst_QNetworkDiskCache::initTestCase() +{ + SubQNetworkDiskCache cache; + cache.setCacheDirectory(QDir::tempPath() + "/diskCache"); + cache.clear(); + QString s = QDir::tempPath() + "/diskCache/"; + QDir dir; + dir.rmdir(s + "data7"); // the number is the internal cache version + dir.rmdir(s + "prepared"); + dir.rmdir(s); + dir.rmdir(s + "http"); // delete directory used by 4.7 and earlier (would make the tests fail) +} + +// This will be called after the last test function is executed. +// It is only called once. +void tst_QNetworkDiskCache::cleanupTestCase() +{ +} + +// This will be called before each test function is executed. +void tst_QNetworkDiskCache::init() +{ +} + +// This will be called after every test function. +void tst_QNetworkDiskCache::cleanup() +{ +} + +void tst_QNetworkDiskCache::qnetworkdiskcache_data() +{ +} + +void tst_QNetworkDiskCache::qnetworkdiskcache() +{ + QUrl url(EXAMPLE_URL); + SubQNetworkDiskCache cache; + QCOMPARE(cache.cacheDirectory(), QString()); + QCOMPARE(cache.cacheSize(), qint64(0)); + cache.clear(); + QCOMPARE(cache.metaData(QUrl()), QNetworkCacheMetaData()); + QCOMPARE(cache.remove(QUrl()), false); + QCOMPARE(cache.remove(url), false); + cache.insert((QIODevice*)0); + cache.setCacheDirectory(QString()); + cache.updateMetaData(QNetworkCacheMetaData()); + cache.prepare(QNetworkCacheMetaData()); + QCOMPARE(cache.call_fileMetaData(QString()), QNetworkCacheMetaData()); + + // leave one hanging around... + QNetworkDiskCache badCache; + QNetworkCacheMetaData metaData; + metaData.setUrl(url); + badCache.prepare(metaData); + badCache.setCacheDirectory(QDir::tempPath() + "/diskCache"); + badCache.prepare(metaData); +} + +void tst_QNetworkDiskCache::prepare() +{ + SubQNetworkDiskCache cache; + cache.setCacheDirectory(QDir::tempPath() + "/diskCache"); + + QUrl url(EXAMPLE_URL); + QNetworkCacheMetaData metaData; + metaData.setUrl(url); + + cache.prepare(metaData); + cache.remove(url); +} + +// public qint64 cacheSize() const +void tst_QNetworkDiskCache::cacheSize() +{ + SubQNetworkDiskCache cache; + cache.setCacheDirectory(QDir::tempPath() + "/diskCache"); + QCOMPARE(cache.cacheSize(), qint64(0)); + + QUrl url(EXAMPLE_URL); + QNetworkCacheMetaData metaData; + metaData.setUrl(url); + QIODevice *d = cache.prepare(metaData); + cache.insert(d); + QVERIFY(cache.cacheSize() > qint64(0)); + + cache.clear(); + QCOMPARE(cache.cacheSize(), qint64(0)); +} + +static QStringList countFiles(const QString dir) +{ + QStringList list; + QDir::Filters filter(QDir::AllEntries | QDir::NoDotAndDotDot); + QDirIterator it(dir, filter, QDirIterator::Subdirectories); + while (it.hasNext()) + list.append(it.next()); + return list; +} + +// public void clear() +void tst_QNetworkDiskCache::clear() +{ + SubQNetworkDiskCache cache; + QUrl url(EXAMPLE_URL); + cache.setupWithOne(url); + QVERIFY(cache.cacheSize() > qint64(0)); + + QString cacheDirectory = cache.cacheDirectory(); + QCOMPARE(countFiles(cacheDirectory).count(), NUM_SUBDIRECTORIES + 3); + cache.clear(); + QCOMPARE(countFiles(cacheDirectory).count(), NUM_SUBDIRECTORIES + 2); + + // don't delete files that it didn't create + QTemporaryFile file(cacheDirectory + "/XXXXXX"); + if (file.open()) { + QCOMPARE(countFiles(cacheDirectory).count(), NUM_SUBDIRECTORIES + 3); + cache.clear(); + QCOMPARE(countFiles(cacheDirectory).count(), NUM_SUBDIRECTORIES + 3); + } +} + +Q_DECLARE_METATYPE(QNetworkCacheMetaData) +void tst_QNetworkDiskCache::data_data() +{ + QTest::addColumn("data"); + + QTest::newRow("null") << QNetworkCacheMetaData(); + + QUrl url(EXAMPLE_URL); + QNetworkCacheMetaData metaData; + metaData.setUrl(url); + QNetworkCacheMetaData::RawHeaderList headers; + headers.append(QNetworkCacheMetaData::RawHeader("type", "bin")); + metaData.setRawHeaders(headers); + QTest::newRow("null") << metaData; +} + +// public QIODevice* data(QUrl const& url) +void tst_QNetworkDiskCache::data() +{ + QFETCH(QNetworkCacheMetaData, data); + SubQNetworkDiskCache cache; + QUrl url(EXAMPLE_URL); + cache.setupWithOne(url, data); + + for (int i = 0; i < 3; ++i) { + QIODevice *d = cache.data(url); + QVERIFY(d); + QCOMPARE(d->readAll(), QByteArray("Hello World!")); + delete d; + } +} + +// public QNetworkCacheMetaData metaData(QUrl const& url) +void tst_QNetworkDiskCache::metaData() +{ + SubQNetworkDiskCache cache; + + QUrl url(EXAMPLE_URL); + QNetworkCacheMetaData metaData; + metaData.setUrl(url); + QNetworkCacheMetaData::RawHeaderList headers; + headers.append(QNetworkCacheMetaData::RawHeader("type", "bin")); + metaData.setRawHeaders(headers); + metaData.setLastModified(QDateTime::currentDateTime()); + metaData.setExpirationDate(QDateTime::currentDateTime()); + metaData.setSaveToDisk(true); + + cache.setupWithOne(url, metaData); + + for (int i = 0; i < 3; ++i) { + QNetworkCacheMetaData cacheMetaData = cache.metaData(url); + QVERIFY(cacheMetaData.isValid()); + QCOMPARE(metaData, cacheMetaData); + } +} + +// public bool remove(QUrl const& url) +void tst_QNetworkDiskCache::remove() +{ + SubQNetworkDiskCache cache; + QUrl url(EXAMPLE_URL); + cache.setupWithOne(url); + QString cacheDirectory = cache.cacheDirectory(); + QCOMPARE(countFiles(cacheDirectory).count(), NUM_SUBDIRECTORIES + 3); + cache.remove(url); + QCOMPARE(countFiles(cacheDirectory).count(), NUM_SUBDIRECTORIES + 2); +} + +void tst_QNetworkDiskCache::setCacheDirectory_data() +{ + QTest::addColumn("cacheDir"); + QTest::newRow("null") << QString(); + QDir dir("foo"); + QTest::newRow("foo") << dir.absolutePath() + QString("/"); +} + +// public void setCacheDirectory(QString const& cacheDir) +void tst_QNetworkDiskCache::setCacheDirectory() +{ + QFETCH(QString, cacheDir); + + SubQNetworkDiskCache cache; + cache.setCacheDirectory(cacheDir); + QCOMPARE(cache.cacheDirectory(), cacheDir); +} + +// public void updateMetaData(QNetworkCacheMetaData const& metaData) +void tst_QNetworkDiskCache::updateMetaData() +{ + QUrl url(EXAMPLE_URL); + SubQNetworkDiskCache cache; + cache.setupWithOne(url); + + QNetworkCacheMetaData metaData = cache.metaData(url); + metaData.setLastModified(QDateTime::currentDateTime()); + cache.updateMetaData(metaData); + QNetworkCacheMetaData newMetaData = cache.metaData(url); + QCOMPARE(newMetaData, metaData); +} + +// protected QNetworkCacheMetaData fileMetaData(QString const& fileName) +void tst_QNetworkDiskCache::fileMetaData() +{ + SubQNetworkDiskCache cache; + QUrl url(EXAMPLE_URL); + cache.setupWithOne(url); + + url.setPassword(QString()); + url.setFragment(QString()); + + QString cacheDirectory = cache.cacheDirectory(); + QStringList list = countFiles(cacheDirectory); + QCOMPARE(list.count(), NUM_SUBDIRECTORIES + 3); + foreach(QString fileName, list) { + QFileInfo info(fileName); + if (info.isFile()) { + QNetworkCacheMetaData metaData = cache.call_fileMetaData(fileName); + QCOMPARE(metaData.url(), url); + } + } + + QTemporaryFile file(cacheDirectory + "/qt_temp.XXXXXX"); + if (file.open()) { + QNetworkCacheMetaData metaData = cache.call_fileMetaData(file.fileName()); + QVERIFY(!metaData.isValid()); + } +} + +// protected qint64 expire() +void tst_QNetworkDiskCache::expire() +{ + SubQNetworkDiskCache cache; + cache.setCacheDirectory(QDir::tempPath() + "/diskCache"); + QCOMPARE(cache.call_expire(), (qint64)0); + QUrl url(EXAMPLE_URL); + cache.setupWithOne(url); + QVERIFY(cache.call_expire() > (qint64)0); + qint64 limit = (1024 * 1024 / 4) * 5; + cache.setMaximumCacheSize(limit); + + qint64 max = cache.maximumCacheSize(); + QCOMPARE(max, limit); + for (int i = 0; i < 10; ++i) { + if (i % 3 == 0) + QTest::qWait(2000); + QNetworkCacheMetaData m; + m.setUrl(QUrl("http://www.foo.com/" + QString::number(i))); + QIODevice *d = cache.prepare(m); + QString bigString; + bigString.fill(QLatin1Char('Z'), (1024 * 1024 / 4)); + d->write(bigString.toLatin1().data()); + cache.insert(d); + QVERIFY(cache.call_expire() < max); + } + + QString cacheDirectory = cache.cacheDirectory(); + QStringList list = countFiles(cacheDirectory); + QStringList cacheList; + foreach(QString fileName, list) { + QFileInfo info(fileName); + if (info.isFile()) { + QNetworkCacheMetaData metaData = cache.call_fileMetaData(fileName); + cacheList.append(metaData.url().toString()); + } + } + qSort(cacheList); + for (int i = 0; i < cacheList.count(); ++i) { + QString fileName = cacheList[i]; + QCOMPARE(fileName, QString("http://www.foo.com/%1").arg(i + 6)); + } +} + +void tst_QNetworkDiskCache::oldCacheVersionFile_data() +{ + QTest::addColumn("pass"); + QTest::newRow("0") << 0; + QTest::newRow("1") << 1; +} + +void tst_QNetworkDiskCache::oldCacheVersionFile() +{ + QFETCH(int, pass); + SubQNetworkDiskCache cache; + QUrl url(EXAMPLE_URL); + cache.setupWithOne(url); + + if (pass == 0) { + QString name; + { + QTemporaryFile file(cache.cacheDirectory() + "/XXXXXX.d"); + file.setAutoRemove(false); + QVERIFY(file.open()); + QDataStream out(&file); + out << qint32(0xe8); + out << qint32(2); + name = file.fileName(); + file.close(); + } + + QVERIFY(QFile::exists(name)); + QNetworkCacheMetaData metaData = cache.call_fileMetaData(name); + QVERIFY(!metaData.isValid()); + QVERIFY(!QFile::exists(name)); + } else { + QStringList files = countFiles(cache.cacheDirectory()); + QCOMPARE(files.count(), NUM_SUBDIRECTORIES + 3); + // find the file + QString cacheFile; + foreach (QString file, files) { + QFileInfo info(file); + if (info.isFile()) + cacheFile = file; + } + QVERIFY(QFile::exists(cacheFile)); + + QFile file(cacheFile); + QVERIFY(file.open(QFile::ReadWrite)); + QDataStream out(&file); + out << qint32(0xe8); + out << qint32(2); + file.close(); + + QIODevice *device = cache.data(url); + QVERIFY(!device); + QVERIFY(!QFile::exists(cacheFile)); + } +} + +class Runner : public QThread +{ + +public: + Runner() + : QThread() + , other(0) + {} + + void run() + { + QByteArray longString = "Hello World, this is some long string, well not really that long"; + for (int j = 0; j < 10; ++j) + longString += longString; + QByteArray longString2 = "Help, I am stuck in an autotest!"; + QUrl url(EXAMPLE_URL); + + QNetworkCacheMetaData metaData; + metaData.setUrl(url); + QNetworkCacheMetaData::RawHeaderList headers; + headers.append(QNetworkCacheMetaData::RawHeader("type", "bin")); + metaData.setRawHeaders(headers); + metaData.setLastModified(dt); + metaData.setSaveToDisk(true); + + QNetworkCacheMetaData metaData2 = metaData; + metaData2.setExpirationDate(dt); + + QNetworkDiskCache cache; + cache.setCacheDirectory(QDir::tempPath() + "/diskCache"); + + int read = 0; + + int i = 0; + for (; i < 5000; ++i) { + if (other && other->isFinished()) + break; + + if (write) { + QNetworkCacheMetaData m; + if (qrand() % 2 == 0) + m = metaData; + else + m = metaData2; + + if (qrand() % 20 == 1) { + //qDebug() << "write update"; + cache.updateMetaData(m); + continue; + } + + QIODevice *device = cache.prepare(m); + if (qrand() % 20 == 1) { + //qDebug() << "write remove"; + cache.remove(url); + continue; + } + QVERIFY(device); + if (qrand() % 2 == 0) + device->write(longString); + else + device->write(longString2); + //qDebug() << "write write" << device->size(); + cache.insert(device); + continue; + } + + QNetworkCacheMetaData gotMetaData = cache.metaData(url); + if (gotMetaData.isValid()) { + QVERIFY(gotMetaData == metaData || gotMetaData == metaData2); + QIODevice *d = cache.data(url); + if (d) { + QByteArray x = d->readAll(); + if (x != longString && x != longString2) { + qDebug() << x.length() << QString(x); + gotMetaData = cache.metaData(url); + qDebug() << (gotMetaData.url().toString()) + << gotMetaData.lastModified() + << gotMetaData.expirationDate() + << gotMetaData.saveToDisk(); + } + if (gotMetaData.isValid()) + QVERIFY(x == longString || x == longString2); + read++; + delete d; + } + } + if (qrand() % 5 == 1) + cache.remove(url); + if (qrand() % 5 == 1) + cache.clear(); + sleep(0); + } + //qDebug() << "read!" << read << i; + } + + QDateTime dt; + bool write; + Runner *other; +}; + +void tst_QNetworkDiskCache::crashWhenParentingCache() +{ + // the trick here is to not send the complete response + // but some data. So we get a readyRead() and it gets tried + // to be saved to the cache + QByteArray data("HTTP/1.0 200 OK\r\nCache-Control: max-age=300\r\nAge: 1\r\nContent-Length: 5\r\n\r\n123"); + MiniHttpServer server(data); + + QNetworkAccessManager *manager = new QNetworkAccessManager(); + QNetworkDiskCache *diskCache = new QNetworkDiskCache(manager); // parent to qnam! + // we expect the temp dir to be cleaned at some point anyway + diskCache->setCacheDirectory(QString("%1/cacheDir_%2").arg(QDir::tempPath()).arg(QCoreApplication::applicationPid())); + manager->setCache(diskCache); + + QUrl url("http://127.0.0.1:" + QString::number(server.serverPort())); + QNetworkRequest request(url); + // request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); + QNetworkReply *reply = manager->get(request); // new reply is parented to qnam + + // wait for readyRead of reply! + connect(reply, SIGNAL(readyRead()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(5); + QVERIFY(!QTestEventLoop::instance().timeout()); + + delete manager; // crashed before.. +} + +void tst_QNetworkDiskCache::sync() +{ + // This tests would be a nice to have, but is currently not supported. + return; + + QTime midnight(0, 0, 0); + qsrand(midnight.secsTo(QTime::currentTime())); + Runner reader; + reader.dt = QDateTime::currentDateTime(); + reader.write = false; + + Runner writer; + writer.dt = reader.dt; + writer.write = true; + + writer.other = &reader; + reader.other = &writer; + + writer.start(); + reader.start(); + writer.wait(); + reader.wait(); +} + +QTEST_MAIN(tst_QNetworkDiskCache) +#include "tst_qnetworkdiskcache.moc" + diff --git a/tests/auto/network/access/qnetworkreply/.gitattributes b/tests/auto/network/access/qnetworkreply/.gitattributes new file mode 100644 index 0000000000..80252cfd34 --- /dev/null +++ b/tests/auto/network/access/qnetworkreply/.gitattributes @@ -0,0 +1,3 @@ +rfc3252.txt -crlf +bigfile -crlf +resource -crlf diff --git a/tests/auto/network/access/qnetworkreply/.gitignore b/tests/auto/network/access/qnetworkreply/.gitignore new file mode 100644 index 0000000000..2797fcd809 --- /dev/null +++ b/tests/auto/network/access/qnetworkreply/.gitignore @@ -0,0 +1,3 @@ +tst_qnetworkreply +echo/echo +echo/echo.exe diff --git a/tests/auto/network/access/qnetworkreply/bigfile b/tests/auto/network/access/qnetworkreply/bigfile new file mode 100644 index 0000000000..cb114a2b0e --- /dev/null +++ b/tests/auto/network/access/qnetworkreply/bigfile @@ -0,0 +1,17980 @@ + + + + + + +Network Working Group H. Kennedy +Request for Comments: 3252 Mimezine +Category: Informational 1 April 2002 + + + Binary Lexical Octet Ad-hoc Transport + +Status of this Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2002). All Rights Reserved. + +Abstract + + This document defines a reformulation of IP and two transport layer + protocols (TCP and UDP) as XML applications. + +1. Introduction + +1.1. Overview + + This document describes the Binary Lexical Octet Ad-hoc Transport + (BLOAT): a reformulation of a widely-deployed network-layer protocol + (IP [RFC791]), and two associated transport layer protocols (TCP + [RFC793] and UDP [RFC768]) as XML [XML] applications. It also + describes methods for transporting BLOAT over Ethernet and IEEE 802 + networks as well as encapsulating BLOAT in IP for gatewaying BLOAT + across the public Internet. + +1.2. Motivation + + The wild popularity of XML as a basis for application-level protocols + such as the Blocks Extensible Exchange Protocol [RFC3080], the Simple + Object Access Protocol [SOAP], and Jabber [JABBER] prompted + investigation into the possibility of extending the use of XML in the + protocol stack. Using XML at both the transport and network layer in + addition to the application layer would provide for an amazing amount + of power and flexibility while removing dependencies on proprietary + and hard-to-understand binary protocols. This protocol unification + would also allow applications to use a single XML parser for all + aspects of their operation, eliminating developer time spent figuring + out the intricacies of each new protocol, and moving the hard work of + + + + +Kennedy Informational [Page 1] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + parsing to the XML toolset. The use of XML also mitigates concerns + over "network vs. host" byte ordering which is at the root of many + network application bugs. + +1.3. Relation to Existing Protocols + + The reformulations specified in this RFC follow as closely as + possible the spirit of the RFCs on which they are based, and so MAY + contain elements or attributes that would not be needed in a pure + reworking (e.g. length attributes, which are implicit in XML.) + + The layering of network and transport protocols are maintained in + this RFC despite the optimizations that could be made if the line + were somewhat blurred (i.e. merging TCP and IP into a single, larger + element in the DTD) in order to foster future use of this protocol as + a basis for reformulating other protocols (such as ICMP.) + + Other than the encoding, the behavioral aspects of each of the + existing protocols remain unchanged. Routing, address spaces, TCP + congestion control, etc. behave as specified in the extant standards. + Adapting to new standards and experimental algorithm heuristics for + improving performance will become much easier once the move to BLOAT + has been completed. + +1.4. Requirement Levels + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in BCP 14, RFC 2119 + [RFC2119]. + +2. IPoXML + + This protocol MUST be implemented to be compliant with this RFC. + IPoXML is the root protocol REQUIRED for effective use of TCPoXML + (section 3.) and higher-level application protocols. + + The DTD for this document type can be found in section 7.1. + + The routing of IPoXML can be easily implemented on hosts with an XML + parser, as the regular structure lends itself handily to parsing and + validation of the document/datagram and then processing the + destination address, TTL, and checksum before sending it on to its + next-hop. + + The reformulation of IPv4 was chosen over IPv6 [RFC2460] due to the + wider deployment of IPv4 and the fact that implementing IPv6 as XML + would have exceeded the 1500 byte Ethernet MTU. + + + +Kennedy Informational [Page 2] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + All BLOAT implementations MUST use - and specify - the UTF-8 encoding + of RFC 2279 [RFC2279]. All BLOAT document/datagrams MUST be well- + formed and include the XMLDecl. + +2.1. IP Description + + A number of items have changed (for the better) from the original IP + specification. Bit-masks, where present have been converted into + human-readable values. IP addresses are listed in their dotted- + decimal notation [RFC1123]. Length and checksum values are present + as decimal integers. + + To calculate the length and checksum fields of the IP element, a + canonicalized form of the element MUST be used. The canonical form + SHALL have no whitespace (including newline characters) between + elements and only one space character between attributes. There + SHALL NOT be a space following the last attribute in an element. + + An iterative method SHOULD be used to calculate checksums, as the + length field will vary based on the size of the checksum. + + The payload element bears special attention. Due to the character + set restrictions of XML, the payload of IP datagrams (which MAY + contain arbitrary data) MUST be encoded for transport. This RFC + REQUIRES the contents of the payload to be encoded in the base-64 + encoding of RFC 2045 [RFC2045], but removes the requirement that the + encoded output MUST be wrapped on 76-character lines. + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 3] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +2.2. Example Datagram + + The following is an example IPoXML datagram with an empty payload: + + + + +
+ + + + + + + + + + + + + + + +
+ + +
+ +3. TCPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.2. + +3.1. TCP Description + + A number of items have changed from the original TCP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + To calculate the length and checksum fields of the TCP element, a + canonicalized form of the element MUST be used as in section 2.1. + + An iterative method SHOULD be used to calculate checksums as in + section 2.1. + + The payload element MUST be encoded as in section 2.1. + + + +Kennedy Informational [Page 4] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + The TCP offset element was expanded to a maximum of 255 from 16 to + allow for the increased size of the header in XML. + + TCPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +3.2. Example Datagram + + The following is an example TCPoXML datagram with an empty payload: + + + + + + + + + + + + + + + + + + + + + + + + +4. UDPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.3. + +4.1. UDP Description + + A number of items have changed from the original UDP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + + + + + + +Kennedy Informational [Page 5] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + To calculate the length and checksum fields of the UDP element, a + canonicalized form of the element MUST be used as in section 2.1. An + iterative method SHOULD be used to calculate checksums as in section + 2.1. + + The payload element MUST be encoded as in section 2.1. + + UDPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +4.2. Example Datagram + + The following is an example UDPoXML datagram with an empty payload: + + + + + + + + + + + + + + +5. Network Transport + + This document provides for the transmission of BLOAT datagrams over + two common families of physical layer transport. Future RFCs will + address additional transports as routing vendors catch up to the + specification, and we begin to see BLOAT routed across the Internet + backbone. + +5.1. Ethernet + + BLOAT is encapsulated in Ethernet datagrams as in [RFC894] with the + exception that the type field of the Ethernet frame MUST contain the + value 0xBEEF. The first 5 octets of the Ethernet frame payload will + be 0x3c 3f 78 6d 6c (" + --> + + + + +Kennedy Informational [Page 7] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 9] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 10] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + +7.2. TCPoXML DTD + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 11] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 12] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + +7.3. UDPoXML DTD + + + + + + + + + + + + + + + +Kennedy Informational [Page 13] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +8. Security Considerations + + XML, as a subset of SGML, has the same security considerations as + specified in SGML Media Types [RFC1874]. Security considerations + that apply to IP, TCP and UDP also likely apply to BLOAT as it does + not attempt to correct for issues not related to message format. + +9. References + + [JABBER] Miller, J., "Jabber", draft-miller-jabber-00.txt, + February 2002. (Work in Progress) + + [RFC768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, + August 1980. + + [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791, + September 1981. + + [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC + 793, September 1981. + + [RFC894] Hornig, C., "Standard for the Transmission of IP + Datagrams over Ethernet Networks.", RFC 894, April 1984. + + [RFC1042] Postel, J. and J. Reynolds, "Standard for the + Transmission of IP Datagrams Over IEEE 802 Networks", STD + 43, RFC 1042, February 1988. + + [RFC1123] Braden, R., "Requirements for Internet Hosts - + Application and Support", RFC 1123, October 1989. + + [RFC1874] Levinson, E., "SGML Media Types", RFC 1874, December + 1995. + + [RFC2003] Perkins, C., "IP Encapsulation within IP", RFC 2003, + October 1996. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", RFC 2279, January 1998. + + + + + +Kennedy Informational [Page 14] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6 + (IPv6) Specification", RFC 2460, December 1998. + + [RFC3080] Rose, M., "The Blocks Extensible Exchange Protocol Core", + RFC 3080, March 2001. + + [SOAP] Box, D., Ehnebuske, D., Kakivaya, G., Layman, A., + Mendelsohn, N., Nielsen, H. F., Thatte, S. Winer, D., + "Simple Object Access Protocol (SOAP) 1.1" World Wide Web + Consortium Note, May 2000 http://www.w3.org/TR/SOAP/ + + [XML] Bray, T., Paoli, J., Sperberg-McQueen, C. M., "Extensible + Markup Language (XML)" World Wide Web Consortium + Recommendation REC- xml-19980210. + http://www.w3.org/TR/1998/REC-xml-19980210 + +10. Author's Address + + Hugh Kennedy + Mimezine + 1060 West Addison + Chicago, IL 60613 + USA + + EMail: kennedyh@engin.umich.edu + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 15] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +11. Full Copyright Statement + + Copyright (C) The Internet Society (2002). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 16] + + + + + + + +Network Working Group H. Kennedy +Request for Comments: 3252 Mimezine +Category: Informational 1 April 2002 + + + Binary Lexical Octet Ad-hoc Transport + +Status of this Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2002). All Rights Reserved. + +Abstract + + This document defines a reformulation of IP and two transport layer + protocols (TCP and UDP) as XML applications. + +1. Introduction + +1.1. Overview + + This document describes the Binary Lexical Octet Ad-hoc Transport + (BLOAT): a reformulation of a widely-deployed network-layer protocol + (IP [RFC791]), and two associated transport layer protocols (TCP + [RFC793] and UDP [RFC768]) as XML [XML] applications. It also + describes methods for transporting BLOAT over Ethernet and IEEE 802 + networks as well as encapsulating BLOAT in IP for gatewaying BLOAT + across the public Internet. + +1.2. Motivation + + The wild popularity of XML as a basis for application-level protocols + such as the Blocks Extensible Exchange Protocol [RFC3080], the Simple + Object Access Protocol [SOAP], and Jabber [JABBER] prompted + investigation into the possibility of extending the use of XML in the + protocol stack. Using XML at both the transport and network layer in + addition to the application layer would provide for an amazing amount + of power and flexibility while removing dependencies on proprietary + and hard-to-understand binary protocols. This protocol unification + would also allow applications to use a single XML parser for all + aspects of their operation, eliminating developer time spent figuring + out the intricacies of each new protocol, and moving the hard work of + + + + +Kennedy Informational [Page 1] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + parsing to the XML toolset. The use of XML also mitigates concerns + over "network vs. host" byte ordering which is at the root of many + network application bugs. + +1.3. Relation to Existing Protocols + + The reformulations specified in this RFC follow as closely as + possible the spirit of the RFCs on which they are based, and so MAY + contain elements or attributes that would not be needed in a pure + reworking (e.g. length attributes, which are implicit in XML.) + + The layering of network and transport protocols are maintained in + this RFC despite the optimizations that could be made if the line + were somewhat blurred (i.e. merging TCP and IP into a single, larger + element in the DTD) in order to foster future use of this protocol as + a basis for reformulating other protocols (such as ICMP.) + + Other than the encoding, the behavioral aspects of each of the + existing protocols remain unchanged. Routing, address spaces, TCP + congestion control, etc. behave as specified in the extant standards. + Adapting to new standards and experimental algorithm heuristics for + improving performance will become much easier once the move to BLOAT + has been completed. + +1.4. Requirement Levels + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in BCP 14, RFC 2119 + [RFC2119]. + +2. IPoXML + + This protocol MUST be implemented to be compliant with this RFC. + IPoXML is the root protocol REQUIRED for effective use of TCPoXML + (section 3.) and higher-level application protocols. + + The DTD for this document type can be found in section 7.1. + + The routing of IPoXML can be easily implemented on hosts with an XML + parser, as the regular structure lends itself handily to parsing and + validation of the document/datagram and then processing the + destination address, TTL, and checksum before sending it on to its + next-hop. + + The reformulation of IPv4 was chosen over IPv6 [RFC2460] due to the + wider deployment of IPv4 and the fact that implementing IPv6 as XML + would have exceeded the 1500 byte Ethernet MTU. + + + +Kennedy Informational [Page 2] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + All BLOAT implementations MUST use - and specify - the UTF-8 encoding + of RFC 2279 [RFC2279]. All BLOAT document/datagrams MUST be well- + formed and include the XMLDecl. + +2.1. IP Description + + A number of items have changed (for the better) from the original IP + specification. Bit-masks, where present have been converted into + human-readable values. IP addresses are listed in their dotted- + decimal notation [RFC1123]. Length and checksum values are present + as decimal integers. + + To calculate the length and checksum fields of the IP element, a + canonicalized form of the element MUST be used. The canonical form + SHALL have no whitespace (including newline characters) between + elements and only one space character between attributes. There + SHALL NOT be a space following the last attribute in an element. + + An iterative method SHOULD be used to calculate checksums, as the + length field will vary based on the size of the checksum. + + The payload element bears special attention. Due to the character + set restrictions of XML, the payload of IP datagrams (which MAY + contain arbitrary data) MUST be encoded for transport. This RFC + REQUIRES the contents of the payload to be encoded in the base-64 + encoding of RFC 2045 [RFC2045], but removes the requirement that the + encoded output MUST be wrapped on 76-character lines. + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 3] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +2.2. Example Datagram + + The following is an example IPoXML datagram with an empty payload: + + + + +
+ + + + + + + + + + + + + + + +
+ + +
+ +3. TCPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.2. + +3.1. TCP Description + + A number of items have changed from the original TCP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + To calculate the length and checksum fields of the TCP element, a + canonicalized form of the element MUST be used as in section 2.1. + + An iterative method SHOULD be used to calculate checksums as in + section 2.1. + + The payload element MUST be encoded as in section 2.1. + + + +Kennedy Informational [Page 4] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + The TCP offset element was expanded to a maximum of 255 from 16 to + allow for the increased size of the header in XML. + + TCPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +3.2. Example Datagram + + The following is an example TCPoXML datagram with an empty payload: + + + + + + + + + + + + + + + + + + + + + + + + +4. UDPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.3. + +4.1. UDP Description + + A number of items have changed from the original UDP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + + + + + + +Kennedy Informational [Page 5] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + To calculate the length and checksum fields of the UDP element, a + canonicalized form of the element MUST be used as in section 2.1. An + iterative method SHOULD be used to calculate checksums as in section + 2.1. + + The payload element MUST be encoded as in section 2.1. + + UDPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +4.2. Example Datagram + + The following is an example UDPoXML datagram with an empty payload: + + + + + + + + + + + + + + +5. Network Transport + + This document provides for the transmission of BLOAT datagrams over + two common families of physical layer transport. Future RFCs will + address additional transports as routing vendors catch up to the + specification, and we begin to see BLOAT routed across the Internet + backbone. + +5.1. Ethernet + + BLOAT is encapsulated in Ethernet datagrams as in [RFC894] with the + exception that the type field of the Ethernet frame MUST contain the + value 0xBEEF. The first 5 octets of the Ethernet frame payload will + be 0x3c 3f 78 6d 6c (" + --> + + + + +Kennedy Informational [Page 7] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 9] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 10] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + +7.2. TCPoXML DTD + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 11] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 12] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + +7.3. UDPoXML DTD + + + + + + + + + + + + + + + +Kennedy Informational [Page 13] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +8. Security Considerations + + XML, as a subset of SGML, has the same security considerations as + specified in SGML Media Types [RFC1874]. Security considerations + that apply to IP, TCP and UDP also likely apply to BLOAT as it does + not attempt to correct for issues not related to message format. + +9. References + + [JABBER] Miller, J., "Jabber", draft-miller-jabber-00.txt, + February 2002. (Work in Progress) + + [RFC768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, + August 1980. + + [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791, + September 1981. + + [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC + 793, September 1981. + + [RFC894] Hornig, C., "Standard for the Transmission of IP + Datagrams over Ethernet Networks.", RFC 894, April 1984. + + [RFC1042] Postel, J. and J. Reynolds, "Standard for the + Transmission of IP Datagrams Over IEEE 802 Networks", STD + 43, RFC 1042, February 1988. + + [RFC1123] Braden, R., "Requirements for Internet Hosts - + Application and Support", RFC 1123, October 1989. + + [RFC1874] Levinson, E., "SGML Media Types", RFC 1874, December + 1995. + + [RFC2003] Perkins, C., "IP Encapsulation within IP", RFC 2003, + October 1996. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", RFC 2279, January 1998. + + + + + +Kennedy Informational [Page 14] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6 + (IPv6) Specification", RFC 2460, December 1998. + + [RFC3080] Rose, M., "The Blocks Extensible Exchange Protocol Core", + RFC 3080, March 2001. + + [SOAP] Box, D., Ehnebuske, D., Kakivaya, G., Layman, A., + Mendelsohn, N., Nielsen, H. F., Thatte, S. Winer, D., + "Simple Object Access Protocol (SOAP) 1.1" World Wide Web + Consortium Note, May 2000 http://www.w3.org/TR/SOAP/ + + [XML] Bray, T., Paoli, J., Sperberg-McQueen, C. M., "Extensible + Markup Language (XML)" World Wide Web Consortium + Recommendation REC- xml-19980210. + http://www.w3.org/TR/1998/REC-xml-19980210 + +10. Author's Address + + Hugh Kennedy + Mimezine + 1060 West Addison + Chicago, IL 60613 + USA + + EMail: kennedyh@engin.umich.edu + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 15] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +11. Full Copyright Statement + + Copyright (C) The Internet Society (2002). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 16] + + + + + + + +Network Working Group H. Kennedy +Request for Comments: 3252 Mimezine +Category: Informational 1 April 2002 + + + Binary Lexical Octet Ad-hoc Transport + +Status of this Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2002). All Rights Reserved. + +Abstract + + This document defines a reformulation of IP and two transport layer + protocols (TCP and UDP) as XML applications. + +1. Introduction + +1.1. Overview + + This document describes the Binary Lexical Octet Ad-hoc Transport + (BLOAT): a reformulation of a widely-deployed network-layer protocol + (IP [RFC791]), and two associated transport layer protocols (TCP + [RFC793] and UDP [RFC768]) as XML [XML] applications. It also + describes methods for transporting BLOAT over Ethernet and IEEE 802 + networks as well as encapsulating BLOAT in IP for gatewaying BLOAT + across the public Internet. + +1.2. Motivation + + The wild popularity of XML as a basis for application-level protocols + such as the Blocks Extensible Exchange Protocol [RFC3080], the Simple + Object Access Protocol [SOAP], and Jabber [JABBER] prompted + investigation into the possibility of extending the use of XML in the + protocol stack. Using XML at both the transport and network layer in + addition to the application layer would provide for an amazing amount + of power and flexibility while removing dependencies on proprietary + and hard-to-understand binary protocols. This protocol unification + would also allow applications to use a single XML parser for all + aspects of their operation, eliminating developer time spent figuring + out the intricacies of each new protocol, and moving the hard work of + + + + +Kennedy Informational [Page 1] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + parsing to the XML toolset. The use of XML also mitigates concerns + over "network vs. host" byte ordering which is at the root of many + network application bugs. + +1.3. Relation to Existing Protocols + + The reformulations specified in this RFC follow as closely as + possible the spirit of the RFCs on which they are based, and so MAY + contain elements or attributes that would not be needed in a pure + reworking (e.g. length attributes, which are implicit in XML.) + + The layering of network and transport protocols are maintained in + this RFC despite the optimizations that could be made if the line + were somewhat blurred (i.e. merging TCP and IP into a single, larger + element in the DTD) in order to foster future use of this protocol as + a basis for reformulating other protocols (such as ICMP.) + + Other than the encoding, the behavioral aspects of each of the + existing protocols remain unchanged. Routing, address spaces, TCP + congestion control, etc. behave as specified in the extant standards. + Adapting to new standards and experimental algorithm heuristics for + improving performance will become much easier once the move to BLOAT + has been completed. + +1.4. Requirement Levels + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in BCP 14, RFC 2119 + [RFC2119]. + +2. IPoXML + + This protocol MUST be implemented to be compliant with this RFC. + IPoXML is the root protocol REQUIRED for effective use of TCPoXML + (section 3.) and higher-level application protocols. + + The DTD for this document type can be found in section 7.1. + + The routing of IPoXML can be easily implemented on hosts with an XML + parser, as the regular structure lends itself handily to parsing and + validation of the document/datagram and then processing the + destination address, TTL, and checksum before sending it on to its + next-hop. + + The reformulation of IPv4 was chosen over IPv6 [RFC2460] due to the + wider deployment of IPv4 and the fact that implementing IPv6 as XML + would have exceeded the 1500 byte Ethernet MTU. + + + +Kennedy Informational [Page 2] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + All BLOAT implementations MUST use - and specify - the UTF-8 encoding + of RFC 2279 [RFC2279]. All BLOAT document/datagrams MUST be well- + formed and include the XMLDecl. + +2.1. IP Description + + A number of items have changed (for the better) from the original IP + specification. Bit-masks, where present have been converted into + human-readable values. IP addresses are listed in their dotted- + decimal notation [RFC1123]. Length and checksum values are present + as decimal integers. + + To calculate the length and checksum fields of the IP element, a + canonicalized form of the element MUST be used. The canonical form + SHALL have no whitespace (including newline characters) between + elements and only one space character between attributes. There + SHALL NOT be a space following the last attribute in an element. + + An iterative method SHOULD be used to calculate checksums, as the + length field will vary based on the size of the checksum. + + The payload element bears special attention. Due to the character + set restrictions of XML, the payload of IP datagrams (which MAY + contain arbitrary data) MUST be encoded for transport. This RFC + REQUIRES the contents of the payload to be encoded in the base-64 + encoding of RFC 2045 [RFC2045], but removes the requirement that the + encoded output MUST be wrapped on 76-character lines. + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 3] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +2.2. Example Datagram + + The following is an example IPoXML datagram with an empty payload: + + + + +
+ + + + + + + + + + + + + + + +
+ + +
+ +3. TCPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.2. + +3.1. TCP Description + + A number of items have changed from the original TCP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + To calculate the length and checksum fields of the TCP element, a + canonicalized form of the element MUST be used as in section 2.1. + + An iterative method SHOULD be used to calculate checksums as in + section 2.1. + + The payload element MUST be encoded as in section 2.1. + + + +Kennedy Informational [Page 4] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + The TCP offset element was expanded to a maximum of 255 from 16 to + allow for the increased size of the header in XML. + + TCPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +3.2. Example Datagram + + The following is an example TCPoXML datagram with an empty payload: + + + + + + + + + + + + + + + + + + + + + + + + +4. UDPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.3. + +4.1. UDP Description + + A number of items have changed from the original UDP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + + + + + + +Kennedy Informational [Page 5] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + To calculate the length and checksum fields of the UDP element, a + canonicalized form of the element MUST be used as in section 2.1. An + iterative method SHOULD be used to calculate checksums as in section + 2.1. + + The payload element MUST be encoded as in section 2.1. + + UDPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +4.2. Example Datagram + + The following is an example UDPoXML datagram with an empty payload: + + + + + + + + + + + + + + +5. Network Transport + + This document provides for the transmission of BLOAT datagrams over + two common families of physical layer transport. Future RFCs will + address additional transports as routing vendors catch up to the + specification, and we begin to see BLOAT routed across the Internet + backbone. + +5.1. Ethernet + + BLOAT is encapsulated in Ethernet datagrams as in [RFC894] with the + exception that the type field of the Ethernet frame MUST contain the + value 0xBEEF. The first 5 octets of the Ethernet frame payload will + be 0x3c 3f 78 6d 6c (" + --> + + + + +Kennedy Informational [Page 7] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 9] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 10] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + +7.2. TCPoXML DTD + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 11] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 12] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + +7.3. UDPoXML DTD + + + + + + + + + + + + + + + +Kennedy Informational [Page 13] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +8. Security Considerations + + XML, as a subset of SGML, has the same security considerations as + specified in SGML Media Types [RFC1874]. Security considerations + that apply to IP, TCP and UDP also likely apply to BLOAT as it does + not attempt to correct for issues not related to message format. + +9. References + + [JABBER] Miller, J., "Jabber", draft-miller-jabber-00.txt, + February 2002. (Work in Progress) + + [RFC768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, + August 1980. + + [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791, + September 1981. + + [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC + 793, September 1981. + + [RFC894] Hornig, C., "Standard for the Transmission of IP + Datagrams over Ethernet Networks.", RFC 894, April 1984. + + [RFC1042] Postel, J. and J. Reynolds, "Standard for the + Transmission of IP Datagrams Over IEEE 802 Networks", STD + 43, RFC 1042, February 1988. + + [RFC1123] Braden, R., "Requirements for Internet Hosts - + Application and Support", RFC 1123, October 1989. + + [RFC1874] Levinson, E., "SGML Media Types", RFC 1874, December + 1995. + + [RFC2003] Perkins, C., "IP Encapsulation within IP", RFC 2003, + October 1996. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", RFC 2279, January 1998. + + + + + +Kennedy Informational [Page 14] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6 + (IPv6) Specification", RFC 2460, December 1998. + + [RFC3080] Rose, M., "The Blocks Extensible Exchange Protocol Core", + RFC 3080, March 2001. + + [SOAP] Box, D., Ehnebuske, D., Kakivaya, G., Layman, A., + Mendelsohn, N., Nielsen, H. F., Thatte, S. Winer, D., + "Simple Object Access Protocol (SOAP) 1.1" World Wide Web + Consortium Note, May 2000 http://www.w3.org/TR/SOAP/ + + [XML] Bray, T., Paoli, J., Sperberg-McQueen, C. M., "Extensible + Markup Language (XML)" World Wide Web Consortium + Recommendation REC- xml-19980210. + http://www.w3.org/TR/1998/REC-xml-19980210 + +10. Author's Address + + Hugh Kennedy + Mimezine + 1060 West Addison + Chicago, IL 60613 + USA + + EMail: kennedyh@engin.umich.edu + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 15] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +11. Full Copyright Statement + + Copyright (C) The Internet Society (2002). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 16] + + + + + + + +Network Working Group H. Kennedy +Request for Comments: 3252 Mimezine +Category: Informational 1 April 2002 + + + Binary Lexical Octet Ad-hoc Transport + +Status of this Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2002). All Rights Reserved. + +Abstract + + This document defines a reformulation of IP and two transport layer + protocols (TCP and UDP) as XML applications. + +1. Introduction + +1.1. Overview + + This document describes the Binary Lexical Octet Ad-hoc Transport + (BLOAT): a reformulation of a widely-deployed network-layer protocol + (IP [RFC791]), and two associated transport layer protocols (TCP + [RFC793] and UDP [RFC768]) as XML [XML] applications. It also + describes methods for transporting BLOAT over Ethernet and IEEE 802 + networks as well as encapsulating BLOAT in IP for gatewaying BLOAT + across the public Internet. + +1.2. Motivation + + The wild popularity of XML as a basis for application-level protocols + such as the Blocks Extensible Exchange Protocol [RFC3080], the Simple + Object Access Protocol [SOAP], and Jabber [JABBER] prompted + investigation into the possibility of extending the use of XML in the + protocol stack. Using XML at both the transport and network layer in + addition to the application layer would provide for an amazing amount + of power and flexibility while removing dependencies on proprietary + and hard-to-understand binary protocols. This protocol unification + would also allow applications to use a single XML parser for all + aspects of their operation, eliminating developer time spent figuring + out the intricacies of each new protocol, and moving the hard work of + + + + +Kennedy Informational [Page 1] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + parsing to the XML toolset. The use of XML also mitigates concerns + over "network vs. host" byte ordering which is at the root of many + network application bugs. + +1.3. Relation to Existing Protocols + + The reformulations specified in this RFC follow as closely as + possible the spirit of the RFCs on which they are based, and so MAY + contain elements or attributes that would not be needed in a pure + reworking (e.g. length attributes, which are implicit in XML.) + + The layering of network and transport protocols are maintained in + this RFC despite the optimizations that could be made if the line + were somewhat blurred (i.e. merging TCP and IP into a single, larger + element in the DTD) in order to foster future use of this protocol as + a basis for reformulating other protocols (such as ICMP.) + + Other than the encoding, the behavioral aspects of each of the + existing protocols remain unchanged. Routing, address spaces, TCP + congestion control, etc. behave as specified in the extant standards. + Adapting to new standards and experimental algorithm heuristics for + improving performance will become much easier once the move to BLOAT + has been completed. + +1.4. Requirement Levels + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in BCP 14, RFC 2119 + [RFC2119]. + +2. IPoXML + + This protocol MUST be implemented to be compliant with this RFC. + IPoXML is the root protocol REQUIRED for effective use of TCPoXML + (section 3.) and higher-level application protocols. + + The DTD for this document type can be found in section 7.1. + + The routing of IPoXML can be easily implemented on hosts with an XML + parser, as the regular structure lends itself handily to parsing and + validation of the document/datagram and then processing the + destination address, TTL, and checksum before sending it on to its + next-hop. + + The reformulation of IPv4 was chosen over IPv6 [RFC2460] due to the + wider deployment of IPv4 and the fact that implementing IPv6 as XML + would have exceeded the 1500 byte Ethernet MTU. + + + +Kennedy Informational [Page 2] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + All BLOAT implementations MUST use - and specify - the UTF-8 encoding + of RFC 2279 [RFC2279]. All BLOAT document/datagrams MUST be well- + formed and include the XMLDecl. + +2.1. IP Description + + A number of items have changed (for the better) from the original IP + specification. Bit-masks, where present have been converted into + human-readable values. IP addresses are listed in their dotted- + decimal notation [RFC1123]. Length and checksum values are present + as decimal integers. + + To calculate the length and checksum fields of the IP element, a + canonicalized form of the element MUST be used. The canonical form + SHALL have no whitespace (including newline characters) between + elements and only one space character between attributes. There + SHALL NOT be a space following the last attribute in an element. + + An iterative method SHOULD be used to calculate checksums, as the + length field will vary based on the size of the checksum. + + The payload element bears special attention. Due to the character + set restrictions of XML, the payload of IP datagrams (which MAY + contain arbitrary data) MUST be encoded for transport. This RFC + REQUIRES the contents of the payload to be encoded in the base-64 + encoding of RFC 2045 [RFC2045], but removes the requirement that the + encoded output MUST be wrapped on 76-character lines. + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 3] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +2.2. Example Datagram + + The following is an example IPoXML datagram with an empty payload: + + + + +
+ + + + + + + + + + + + + + + +
+ + +
+ +3. TCPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.2. + +3.1. TCP Description + + A number of items have changed from the original TCP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + To calculate the length and checksum fields of the TCP element, a + canonicalized form of the element MUST be used as in section 2.1. + + An iterative method SHOULD be used to calculate checksums as in + section 2.1. + + The payload element MUST be encoded as in section 2.1. + + + +Kennedy Informational [Page 4] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + The TCP offset element was expanded to a maximum of 255 from 16 to + allow for the increased size of the header in XML. + + TCPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +3.2. Example Datagram + + The following is an example TCPoXML datagram with an empty payload: + + + + + + + + + + + + + + + + + + + + + + + + +4. UDPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.3. + +4.1. UDP Description + + A number of items have changed from the original UDP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + + + + + + +Kennedy Informational [Page 5] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + To calculate the length and checksum fields of the UDP element, a + canonicalized form of the element MUST be used as in section 2.1. An + iterative method SHOULD be used to calculate checksums as in section + 2.1. + + The payload element MUST be encoded as in section 2.1. + + UDPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +4.2. Example Datagram + + The following is an example UDPoXML datagram with an empty payload: + + + + + + + + + + + + + + +5. Network Transport + + This document provides for the transmission of BLOAT datagrams over + two common families of physical layer transport. Future RFCs will + address additional transports as routing vendors catch up to the + specification, and we begin to see BLOAT routed across the Internet + backbone. + +5.1. Ethernet + + BLOAT is encapsulated in Ethernet datagrams as in [RFC894] with the + exception that the type field of the Ethernet frame MUST contain the + value 0xBEEF. The first 5 octets of the Ethernet frame payload will + be 0x3c 3f 78 6d 6c (" + --> + + + + +Kennedy Informational [Page 7] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 9] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 10] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + +7.2. TCPoXML DTD + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 11] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 12] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + +7.3. UDPoXML DTD + + + + + + + + + + + + + + + +Kennedy Informational [Page 13] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +8. Security Considerations + + XML, as a subset of SGML, has the same security considerations as + specified in SGML Media Types [RFC1874]. Security considerations + that apply to IP, TCP and UDP also likely apply to BLOAT as it does + not attempt to correct for issues not related to message format. + +9. References + + [JABBER] Miller, J., "Jabber", draft-miller-jabber-00.txt, + February 2002. (Work in Progress) + + [RFC768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, + August 1980. + + [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791, + September 1981. + + [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC + 793, September 1981. + + [RFC894] Hornig, C., "Standard for the Transmission of IP + Datagrams over Ethernet Networks.", RFC 894, April 1984. + + [RFC1042] Postel, J. and J. Reynolds, "Standard for the + Transmission of IP Datagrams Over IEEE 802 Networks", STD + 43, RFC 1042, February 1988. + + [RFC1123] Braden, R., "Requirements for Internet Hosts - + Application and Support", RFC 1123, October 1989. + + [RFC1874] Levinson, E., "SGML Media Types", RFC 1874, December + 1995. + + [RFC2003] Perkins, C., "IP Encapsulation within IP", RFC 2003, + October 1996. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", RFC 2279, January 1998. + + + + + +Kennedy Informational [Page 14] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6 + (IPv6) Specification", RFC 2460, December 1998. + + [RFC3080] Rose, M., "The Blocks Extensible Exchange Protocol Core", + RFC 3080, March 2001. + + [SOAP] Box, D., Ehnebuske, D., Kakivaya, G., Layman, A., + Mendelsohn, N., Nielsen, H. F., Thatte, S. Winer, D., + "Simple Object Access Protocol (SOAP) 1.1" World Wide Web + Consortium Note, May 2000 http://www.w3.org/TR/SOAP/ + + [XML] Bray, T., Paoli, J., Sperberg-McQueen, C. M., "Extensible + Markup Language (XML)" World Wide Web Consortium + Recommendation REC- xml-19980210. + http://www.w3.org/TR/1998/REC-xml-19980210 + +10. Author's Address + + Hugh Kennedy + Mimezine + 1060 West Addison + Chicago, IL 60613 + USA + + EMail: kennedyh@engin.umich.edu + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 15] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +11. Full Copyright Statement + + Copyright (C) The Internet Society (2002). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 16] + + + + + + + +Network Working Group H. Kennedy +Request for Comments: 3252 Mimezine +Category: Informational 1 April 2002 + + + Binary Lexical Octet Ad-hoc Transport + +Status of this Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2002). All Rights Reserved. + +Abstract + + This document defines a reformulation of IP and two transport layer + protocols (TCP and UDP) as XML applications. + +1. Introduction + +1.1. Overview + + This document describes the Binary Lexical Octet Ad-hoc Transport + (BLOAT): a reformulation of a widely-deployed network-layer protocol + (IP [RFC791]), and two associated transport layer protocols (TCP + [RFC793] and UDP [RFC768]) as XML [XML] applications. It also + describes methods for transporting BLOAT over Ethernet and IEEE 802 + networks as well as encapsulating BLOAT in IP for gatewaying BLOAT + across the public Internet. + +1.2. Motivation + + The wild popularity of XML as a basis for application-level protocols + such as the Blocks Extensible Exchange Protocol [RFC3080], the Simple + Object Access Protocol [SOAP], and Jabber [JABBER] prompted + investigation into the possibility of extending the use of XML in the + protocol stack. Using XML at both the transport and network layer in + addition to the application layer would provide for an amazing amount + of power and flexibility while removing dependencies on proprietary + and hard-to-understand binary protocols. This protocol unification + would also allow applications to use a single XML parser for all + aspects of their operation, eliminating developer time spent figuring + out the intricacies of each new protocol, and moving the hard work of + + + + +Kennedy Informational [Page 1] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + parsing to the XML toolset. The use of XML also mitigates concerns + over "network vs. host" byte ordering which is at the root of many + network application bugs. + +1.3. Relation to Existing Protocols + + The reformulations specified in this RFC follow as closely as + possible the spirit of the RFCs on which they are based, and so MAY + contain elements or attributes that would not be needed in a pure + reworking (e.g. length attributes, which are implicit in XML.) + + The layering of network and transport protocols are maintained in + this RFC despite the optimizations that could be made if the line + were somewhat blurred (i.e. merging TCP and IP into a single, larger + element in the DTD) in order to foster future use of this protocol as + a basis for reformulating other protocols (such as ICMP.) + + Other than the encoding, the behavioral aspects of each of the + existing protocols remain unchanged. Routing, address spaces, TCP + congestion control, etc. behave as specified in the extant standards. + Adapting to new standards and experimental algorithm heuristics for + improving performance will become much easier once the move to BLOAT + has been completed. + +1.4. Requirement Levels + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in BCP 14, RFC 2119 + [RFC2119]. + +2. IPoXML + + This protocol MUST be implemented to be compliant with this RFC. + IPoXML is the root protocol REQUIRED for effective use of TCPoXML + (section 3.) and higher-level application protocols. + + The DTD for this document type can be found in section 7.1. + + The routing of IPoXML can be easily implemented on hosts with an XML + parser, as the regular structure lends itself handily to parsing and + validation of the document/datagram and then processing the + destination address, TTL, and checksum before sending it on to its + next-hop. + + The reformulation of IPv4 was chosen over IPv6 [RFC2460] due to the + wider deployment of IPv4 and the fact that implementing IPv6 as XML + would have exceeded the 1500 byte Ethernet MTU. + + + +Kennedy Informational [Page 2] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + All BLOAT implementations MUST use - and specify - the UTF-8 encoding + of RFC 2279 [RFC2279]. All BLOAT document/datagrams MUST be well- + formed and include the XMLDecl. + +2.1. IP Description + + A number of items have changed (for the better) from the original IP + specification. Bit-masks, where present have been converted into + human-readable values. IP addresses are listed in their dotted- + decimal notation [RFC1123]. Length and checksum values are present + as decimal integers. + + To calculate the length and checksum fields of the IP element, a + canonicalized form of the element MUST be used. The canonical form + SHALL have no whitespace (including newline characters) between + elements and only one space character between attributes. There + SHALL NOT be a space following the last attribute in an element. + + An iterative method SHOULD be used to calculate checksums, as the + length field will vary based on the size of the checksum. + + The payload element bears special attention. Due to the character + set restrictions of XML, the payload of IP datagrams (which MAY + contain arbitrary data) MUST be encoded for transport. This RFC + REQUIRES the contents of the payload to be encoded in the base-64 + encoding of RFC 2045 [RFC2045], but removes the requirement that the + encoded output MUST be wrapped on 76-character lines. + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 3] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +2.2. Example Datagram + + The following is an example IPoXML datagram with an empty payload: + + + + +
+ + + + + + + + + + + + + + + +
+ + +
+ +3. TCPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.2. + +3.1. TCP Description + + A number of items have changed from the original TCP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + To calculate the length and checksum fields of the TCP element, a + canonicalized form of the element MUST be used as in section 2.1. + + An iterative method SHOULD be used to calculate checksums as in + section 2.1. + + The payload element MUST be encoded as in section 2.1. + + + +Kennedy Informational [Page 4] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + The TCP offset element was expanded to a maximum of 255 from 16 to + allow for the increased size of the header in XML. + + TCPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +3.2. Example Datagram + + The following is an example TCPoXML datagram with an empty payload: + + + + + + + + + + + + + + + + + + + + + + + + +4. UDPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.3. + +4.1. UDP Description + + A number of items have changed from the original UDP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + + + + + + +Kennedy Informational [Page 5] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + To calculate the length and checksum fields of the UDP element, a + canonicalized form of the element MUST be used as in section 2.1. An + iterative method SHOULD be used to calculate checksums as in section + 2.1. + + The payload element MUST be encoded as in section 2.1. + + UDPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +4.2. Example Datagram + + The following is an example UDPoXML datagram with an empty payload: + + + + + + + + + + + + + + +5. Network Transport + + This document provides for the transmission of BLOAT datagrams over + two common families of physical layer transport. Future RFCs will + address additional transports as routing vendors catch up to the + specification, and we begin to see BLOAT routed across the Internet + backbone. + +5.1. Ethernet + + BLOAT is encapsulated in Ethernet datagrams as in [RFC894] with the + exception that the type field of the Ethernet frame MUST contain the + value 0xBEEF. The first 5 octets of the Ethernet frame payload will + be 0x3c 3f 78 6d 6c (" + --> + + + + +Kennedy Informational [Page 7] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 9] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 10] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + +7.2. TCPoXML DTD + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 11] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 12] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + +7.3. UDPoXML DTD + + + + + + + + + + + + + + + +Kennedy Informational [Page 13] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +8. Security Considerations + + XML, as a subset of SGML, has the same security considerations as + specified in SGML Media Types [RFC1874]. Security considerations + that apply to IP, TCP and UDP also likely apply to BLOAT as it does + not attempt to correct for issues not related to message format. + +9. References + + [JABBER] Miller, J., "Jabber", draft-miller-jabber-00.txt, + February 2002. (Work in Progress) + + [RFC768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, + August 1980. + + [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791, + September 1981. + + [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC + 793, September 1981. + + [RFC894] Hornig, C., "Standard for the Transmission of IP + Datagrams over Ethernet Networks.", RFC 894, April 1984. + + [RFC1042] Postel, J. and J. Reynolds, "Standard for the + Transmission of IP Datagrams Over IEEE 802 Networks", STD + 43, RFC 1042, February 1988. + + [RFC1123] Braden, R., "Requirements for Internet Hosts - + Application and Support", RFC 1123, October 1989. + + [RFC1874] Levinson, E., "SGML Media Types", RFC 1874, December + 1995. + + [RFC2003] Perkins, C., "IP Encapsulation within IP", RFC 2003, + October 1996. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", RFC 2279, January 1998. + + + + + +Kennedy Informational [Page 14] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6 + (IPv6) Specification", RFC 2460, December 1998. + + [RFC3080] Rose, M., "The Blocks Extensible Exchange Protocol Core", + RFC 3080, March 2001. + + [SOAP] Box, D., Ehnebuske, D., Kakivaya, G., Layman, A., + Mendelsohn, N., Nielsen, H. F., Thatte, S. Winer, D., + "Simple Object Access Protocol (SOAP) 1.1" World Wide Web + Consortium Note, May 2000 http://www.w3.org/TR/SOAP/ + + [XML] Bray, T., Paoli, J., Sperberg-McQueen, C. M., "Extensible + Markup Language (XML)" World Wide Web Consortium + Recommendation REC- xml-19980210. + http://www.w3.org/TR/1998/REC-xml-19980210 + +10. Author's Address + + Hugh Kennedy + Mimezine + 1060 West Addison + Chicago, IL 60613 + USA + + EMail: kennedyh@engin.umich.edu + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 15] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +11. Full Copyright Statement + + Copyright (C) The Internet Society (2002). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 16] + + + + + + + +Network Working Group H. Kennedy +Request for Comments: 3252 Mimezine +Category: Informational 1 April 2002 + + + Binary Lexical Octet Ad-hoc Transport + +Status of this Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2002). All Rights Reserved. + +Abstract + + This document defines a reformulation of IP and two transport layer + protocols (TCP and UDP) as XML applications. + +1. Introduction + +1.1. Overview + + This document describes the Binary Lexical Octet Ad-hoc Transport + (BLOAT): a reformulation of a widely-deployed network-layer protocol + (IP [RFC791]), and two associated transport layer protocols (TCP + [RFC793] and UDP [RFC768]) as XML [XML] applications. It also + describes methods for transporting BLOAT over Ethernet and IEEE 802 + networks as well as encapsulating BLOAT in IP for gatewaying BLOAT + across the public Internet. + +1.2. Motivation + + The wild popularity of XML as a basis for application-level protocols + such as the Blocks Extensible Exchange Protocol [RFC3080], the Simple + Object Access Protocol [SOAP], and Jabber [JABBER] prompted + investigation into the possibility of extending the use of XML in the + protocol stack. Using XML at both the transport and network layer in + addition to the application layer would provide for an amazing amount + of power and flexibility while removing dependencies on proprietary + and hard-to-understand binary protocols. This protocol unification + would also allow applications to use a single XML parser for all + aspects of their operation, eliminating developer time spent figuring + out the intricacies of each new protocol, and moving the hard work of + + + + +Kennedy Informational [Page 1] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + parsing to the XML toolset. The use of XML also mitigates concerns + over "network vs. host" byte ordering which is at the root of many + network application bugs. + +1.3. Relation to Existing Protocols + + The reformulations specified in this RFC follow as closely as + possible the spirit of the RFCs on which they are based, and so MAY + contain elements or attributes that would not be needed in a pure + reworking (e.g. length attributes, which are implicit in XML.) + + The layering of network and transport protocols are maintained in + this RFC despite the optimizations that could be made if the line + were somewhat blurred (i.e. merging TCP and IP into a single, larger + element in the DTD) in order to foster future use of this protocol as + a basis for reformulating other protocols (such as ICMP.) + + Other than the encoding, the behavioral aspects of each of the + existing protocols remain unchanged. Routing, address spaces, TCP + congestion control, etc. behave as specified in the extant standards. + Adapting to new standards and experimental algorithm heuristics for + improving performance will become much easier once the move to BLOAT + has been completed. + +1.4. Requirement Levels + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in BCP 14, RFC 2119 + [RFC2119]. + +2. IPoXML + + This protocol MUST be implemented to be compliant with this RFC. + IPoXML is the root protocol REQUIRED for effective use of TCPoXML + (section 3.) and higher-level application protocols. + + The DTD for this document type can be found in section 7.1. + + The routing of IPoXML can be easily implemented on hosts with an XML + parser, as the regular structure lends itself handily to parsing and + validation of the document/datagram and then processing the + destination address, TTL, and checksum before sending it on to its + next-hop. + + The reformulation of IPv4 was chosen over IPv6 [RFC2460] due to the + wider deployment of IPv4 and the fact that implementing IPv6 as XML + would have exceeded the 1500 byte Ethernet MTU. + + + +Kennedy Informational [Page 2] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + All BLOAT implementations MUST use - and specify - the UTF-8 encoding + of RFC 2279 [RFC2279]. All BLOAT document/datagrams MUST be well- + formed and include the XMLDecl. + +2.1. IP Description + + A number of items have changed (for the better) from the original IP + specification. Bit-masks, where present have been converted into + human-readable values. IP addresses are listed in their dotted- + decimal notation [RFC1123]. Length and checksum values are present + as decimal integers. + + To calculate the length and checksum fields of the IP element, a + canonicalized form of the element MUST be used. The canonical form + SHALL have no whitespace (including newline characters) between + elements and only one space character between attributes. There + SHALL NOT be a space following the last attribute in an element. + + An iterative method SHOULD be used to calculate checksums, as the + length field will vary based on the size of the checksum. + + The payload element bears special attention. Due to the character + set restrictions of XML, the payload of IP datagrams (which MAY + contain arbitrary data) MUST be encoded for transport. This RFC + REQUIRES the contents of the payload to be encoded in the base-64 + encoding of RFC 2045 [RFC2045], but removes the requirement that the + encoded output MUST be wrapped on 76-character lines. + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 3] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +2.2. Example Datagram + + The following is an example IPoXML datagram with an empty payload: + + + + +
+ + + + + + + + + + + + + + + +
+ + +
+ +3. TCPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.2. + +3.1. TCP Description + + A number of items have changed from the original TCP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + To calculate the length and checksum fields of the TCP element, a + canonicalized form of the element MUST be used as in section 2.1. + + An iterative method SHOULD be used to calculate checksums as in + section 2.1. + + The payload element MUST be encoded as in section 2.1. + + + +Kennedy Informational [Page 4] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + The TCP offset element was expanded to a maximum of 255 from 16 to + allow for the increased size of the header in XML. + + TCPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +3.2. Example Datagram + + The following is an example TCPoXML datagram with an empty payload: + + + + + + + + + + + + + + + + + + + + + + + + +4. UDPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.3. + +4.1. UDP Description + + A number of items have changed from the original UDP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + + + + + + +Kennedy Informational [Page 5] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + To calculate the length and checksum fields of the UDP element, a + canonicalized form of the element MUST be used as in section 2.1. An + iterative method SHOULD be used to calculate checksums as in section + 2.1. + + The payload element MUST be encoded as in section 2.1. + + UDPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +4.2. Example Datagram + + The following is an example UDPoXML datagram with an empty payload: + + + + + + + + + + + + + + +5. Network Transport + + This document provides for the transmission of BLOAT datagrams over + two common families of physical layer transport. Future RFCs will + address additional transports as routing vendors catch up to the + specification, and we begin to see BLOAT routed across the Internet + backbone. + +5.1. Ethernet + + BLOAT is encapsulated in Ethernet datagrams as in [RFC894] with the + exception that the type field of the Ethernet frame MUST contain the + value 0xBEEF. The first 5 octets of the Ethernet frame payload will + be 0x3c 3f 78 6d 6c (" + --> + + + + +Kennedy Informational [Page 7] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 9] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 10] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + +7.2. TCPoXML DTD + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 11] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 12] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + +7.3. UDPoXML DTD + + + + + + + + + + + + + + + +Kennedy Informational [Page 13] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +8. Security Considerations + + XML, as a subset of SGML, has the same security considerations as + specified in SGML Media Types [RFC1874]. Security considerations + that apply to IP, TCP and UDP also likely apply to BLOAT as it does + not attempt to correct for issues not related to message format. + +9. References + + [JABBER] Miller, J., "Jabber", draft-miller-jabber-00.txt, + February 2002. (Work in Progress) + + [RFC768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, + August 1980. + + [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791, + September 1981. + + [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC + 793, September 1981. + + [RFC894] Hornig, C., "Standard for the Transmission of IP + Datagrams over Ethernet Networks.", RFC 894, April 1984. + + [RFC1042] Postel, J. and J. Reynolds, "Standard for the + Transmission of IP Datagrams Over IEEE 802 Networks", STD + 43, RFC 1042, February 1988. + + [RFC1123] Braden, R., "Requirements for Internet Hosts - + Application and Support", RFC 1123, October 1989. + + [RFC1874] Levinson, E., "SGML Media Types", RFC 1874, December + 1995. + + [RFC2003] Perkins, C., "IP Encapsulation within IP", RFC 2003, + October 1996. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", RFC 2279, January 1998. + + + + + +Kennedy Informational [Page 14] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6 + (IPv6) Specification", RFC 2460, December 1998. + + [RFC3080] Rose, M., "The Blocks Extensible Exchange Protocol Core", + RFC 3080, March 2001. + + [SOAP] Box, D., Ehnebuske, D., Kakivaya, G., Layman, A., + Mendelsohn, N., Nielsen, H. F., Thatte, S. Winer, D., + "Simple Object Access Protocol (SOAP) 1.1" World Wide Web + Consortium Note, May 2000 http://www.w3.org/TR/SOAP/ + + [XML] Bray, T., Paoli, J., Sperberg-McQueen, C. M., "Extensible + Markup Language (XML)" World Wide Web Consortium + Recommendation REC- xml-19980210. + http://www.w3.org/TR/1998/REC-xml-19980210 + +10. Author's Address + + Hugh Kennedy + Mimezine + 1060 West Addison + Chicago, IL 60613 + USA + + EMail: kennedyh@engin.umich.edu + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 15] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +11. Full Copyright Statement + + Copyright (C) The Internet Society (2002). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 16] + + + + + + + +Network Working Group H. Kennedy +Request for Comments: 3252 Mimezine +Category: Informational 1 April 2002 + + + Binary Lexical Octet Ad-hoc Transport + +Status of this Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2002). All Rights Reserved. + +Abstract + + This document defines a reformulation of IP and two transport layer + protocols (TCP and UDP) as XML applications. + +1. Introduction + +1.1. Overview + + This document describes the Binary Lexical Octet Ad-hoc Transport + (BLOAT): a reformulation of a widely-deployed network-layer protocol + (IP [RFC791]), and two associated transport layer protocols (TCP + [RFC793] and UDP [RFC768]) as XML [XML] applications. It also + describes methods for transporting BLOAT over Ethernet and IEEE 802 + networks as well as encapsulating BLOAT in IP for gatewaying BLOAT + across the public Internet. + +1.2. Motivation + + The wild popularity of XML as a basis for application-level protocols + such as the Blocks Extensible Exchange Protocol [RFC3080], the Simple + Object Access Protocol [SOAP], and Jabber [JABBER] prompted + investigation into the possibility of extending the use of XML in the + protocol stack. Using XML at both the transport and network layer in + addition to the application layer would provide for an amazing amount + of power and flexibility while removing dependencies on proprietary + and hard-to-understand binary protocols. This protocol unification + would also allow applications to use a single XML parser for all + aspects of their operation, eliminating developer time spent figuring + out the intricacies of each new protocol, and moving the hard work of + + + + +Kennedy Informational [Page 1] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + parsing to the XML toolset. The use of XML also mitigates concerns + over "network vs. host" byte ordering which is at the root of many + network application bugs. + +1.3. Relation to Existing Protocols + + The reformulations specified in this RFC follow as closely as + possible the spirit of the RFCs on which they are based, and so MAY + contain elements or attributes that would not be needed in a pure + reworking (e.g. length attributes, which are implicit in XML.) + + The layering of network and transport protocols are maintained in + this RFC despite the optimizations that could be made if the line + were somewhat blurred (i.e. merging TCP and IP into a single, larger + element in the DTD) in order to foster future use of this protocol as + a basis for reformulating other protocols (such as ICMP.) + + Other than the encoding, the behavioral aspects of each of the + existing protocols remain unchanged. Routing, address spaces, TCP + congestion control, etc. behave as specified in the extant standards. + Adapting to new standards and experimental algorithm heuristics for + improving performance will become much easier once the move to BLOAT + has been completed. + +1.4. Requirement Levels + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in BCP 14, RFC 2119 + [RFC2119]. + +2. IPoXML + + This protocol MUST be implemented to be compliant with this RFC. + IPoXML is the root protocol REQUIRED for effective use of TCPoXML + (section 3.) and higher-level application protocols. + + The DTD for this document type can be found in section 7.1. + + The routing of IPoXML can be easily implemented on hosts with an XML + parser, as the regular structure lends itself handily to parsing and + validation of the document/datagram and then processing the + destination address, TTL, and checksum before sending it on to its + next-hop. + + The reformulation of IPv4 was chosen over IPv6 [RFC2460] due to the + wider deployment of IPv4 and the fact that implementing IPv6 as XML + would have exceeded the 1500 byte Ethernet MTU. + + + +Kennedy Informational [Page 2] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + All BLOAT implementations MUST use - and specify - the UTF-8 encoding + of RFC 2279 [RFC2279]. All BLOAT document/datagrams MUST be well- + formed and include the XMLDecl. + +2.1. IP Description + + A number of items have changed (for the better) from the original IP + specification. Bit-masks, where present have been converted into + human-readable values. IP addresses are listed in their dotted- + decimal notation [RFC1123]. Length and checksum values are present + as decimal integers. + + To calculate the length and checksum fields of the IP element, a + canonicalized form of the element MUST be used. The canonical form + SHALL have no whitespace (including newline characters) between + elements and only one space character between attributes. There + SHALL NOT be a space following the last attribute in an element. + + An iterative method SHOULD be used to calculate checksums, as the + length field will vary based on the size of the checksum. + + The payload element bears special attention. Due to the character + set restrictions of XML, the payload of IP datagrams (which MAY + contain arbitrary data) MUST be encoded for transport. This RFC + REQUIRES the contents of the payload to be encoded in the base-64 + encoding of RFC 2045 [RFC2045], but removes the requirement that the + encoded output MUST be wrapped on 76-character lines. + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 3] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +2.2. Example Datagram + + The following is an example IPoXML datagram with an empty payload: + + + + +
+ + + + + + + + + + + + + + + +
+ + +
+ +3. TCPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.2. + +3.1. TCP Description + + A number of items have changed from the original TCP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + To calculate the length and checksum fields of the TCP element, a + canonicalized form of the element MUST be used as in section 2.1. + + An iterative method SHOULD be used to calculate checksums as in + section 2.1. + + The payload element MUST be encoded as in section 2.1. + + + +Kennedy Informational [Page 4] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + The TCP offset element was expanded to a maximum of 255 from 16 to + allow for the increased size of the header in XML. + + TCPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +3.2. Example Datagram + + The following is an example TCPoXML datagram with an empty payload: + + + + + + + + + + + + + + + + + + + + + + + + +4. UDPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.3. + +4.1. UDP Description + + A number of items have changed from the original UDP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + + + + + + +Kennedy Informational [Page 5] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + To calculate the length and checksum fields of the UDP element, a + canonicalized form of the element MUST be used as in section 2.1. An + iterative method SHOULD be used to calculate checksums as in section + 2.1. + + The payload element MUST be encoded as in section 2.1. + + UDPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +4.2. Example Datagram + + The following is an example UDPoXML datagram with an empty payload: + + + + + + + + + + + + + + +5. Network Transport + + This document provides for the transmission of BLOAT datagrams over + two common families of physical layer transport. Future RFCs will + address additional transports as routing vendors catch up to the + specification, and we begin to see BLOAT routed across the Internet + backbone. + +5.1. Ethernet + + BLOAT is encapsulated in Ethernet datagrams as in [RFC894] with the + exception that the type field of the Ethernet frame MUST contain the + value 0xBEEF. The first 5 octets of the Ethernet frame payload will + be 0x3c 3f 78 6d 6c (" + --> + + + + +Kennedy Informational [Page 7] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 9] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 10] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + +7.2. TCPoXML DTD + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 11] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 12] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + +7.3. UDPoXML DTD + + + + + + + + + + + + + + + +Kennedy Informational [Page 13] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +8. Security Considerations + + XML, as a subset of SGML, has the same security considerations as + specified in SGML Media Types [RFC1874]. Security considerations + that apply to IP, TCP and UDP also likely apply to BLOAT as it does + not attempt to correct for issues not related to message format. + +9. References + + [JABBER] Miller, J., "Jabber", draft-miller-jabber-00.txt, + February 2002. (Work in Progress) + + [RFC768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, + August 1980. + + [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791, + September 1981. + + [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC + 793, September 1981. + + [RFC894] Hornig, C., "Standard for the Transmission of IP + Datagrams over Ethernet Networks.", RFC 894, April 1984. + + [RFC1042] Postel, J. and J. Reynolds, "Standard for the + Transmission of IP Datagrams Over IEEE 802 Networks", STD + 43, RFC 1042, February 1988. + + [RFC1123] Braden, R., "Requirements for Internet Hosts - + Application and Support", RFC 1123, October 1989. + + [RFC1874] Levinson, E., "SGML Media Types", RFC 1874, December + 1995. + + [RFC2003] Perkins, C., "IP Encapsulation within IP", RFC 2003, + October 1996. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", RFC 2279, January 1998. + + + + + +Kennedy Informational [Page 14] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6 + (IPv6) Specification", RFC 2460, December 1998. + + [RFC3080] Rose, M., "The Blocks Extensible Exchange Protocol Core", + RFC 3080, March 2001. + + [SOAP] Box, D., Ehnebuske, D., Kakivaya, G., Layman, A., + Mendelsohn, N., Nielsen, H. F., Thatte, S. Winer, D., + "Simple Object Access Protocol (SOAP) 1.1" World Wide Web + Consortium Note, May 2000 http://www.w3.org/TR/SOAP/ + + [XML] Bray, T., Paoli, J., Sperberg-McQueen, C. M., "Extensible + Markup Language (XML)" World Wide Web Consortium + Recommendation REC- xml-19980210. + http://www.w3.org/TR/1998/REC-xml-19980210 + +10. Author's Address + + Hugh Kennedy + Mimezine + 1060 West Addison + Chicago, IL 60613 + USA + + EMail: kennedyh@engin.umich.edu + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 15] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +11. Full Copyright Statement + + Copyright (C) The Internet Society (2002). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 16] + + + + + + + +Network Working Group H. Kennedy +Request for Comments: 3252 Mimezine +Category: Informational 1 April 2002 + + + Binary Lexical Octet Ad-hoc Transport + +Status of this Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2002). All Rights Reserved. + +Abstract + + This document defines a reformulation of IP and two transport layer + protocols (TCP and UDP) as XML applications. + +1. Introduction + +1.1. Overview + + This document describes the Binary Lexical Octet Ad-hoc Transport + (BLOAT): a reformulation of a widely-deployed network-layer protocol + (IP [RFC791]), and two associated transport layer protocols (TCP + [RFC793] and UDP [RFC768]) as XML [XML] applications. It also + describes methods for transporting BLOAT over Ethernet and IEEE 802 + networks as well as encapsulating BLOAT in IP for gatewaying BLOAT + across the public Internet. + +1.2. Motivation + + The wild popularity of XML as a basis for application-level protocols + such as the Blocks Extensible Exchange Protocol [RFC3080], the Simple + Object Access Protocol [SOAP], and Jabber [JABBER] prompted + investigation into the possibility of extending the use of XML in the + protocol stack. Using XML at both the transport and network layer in + addition to the application layer would provide for an amazing amount + of power and flexibility while removing dependencies on proprietary + and hard-to-understand binary protocols. This protocol unification + would also allow applications to use a single XML parser for all + aspects of their operation, eliminating developer time spent figuring + out the intricacies of each new protocol, and moving the hard work of + + + + +Kennedy Informational [Page 1] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + parsing to the XML toolset. The use of XML also mitigates concerns + over "network vs. host" byte ordering which is at the root of many + network application bugs. + +1.3. Relation to Existing Protocols + + The reformulations specified in this RFC follow as closely as + possible the spirit of the RFCs on which they are based, and so MAY + contain elements or attributes that would not be needed in a pure + reworking (e.g. length attributes, which are implicit in XML.) + + The layering of network and transport protocols are maintained in + this RFC despite the optimizations that could be made if the line + were somewhat blurred (i.e. merging TCP and IP into a single, larger + element in the DTD) in order to foster future use of this protocol as + a basis for reformulating other protocols (such as ICMP.) + + Other than the encoding, the behavioral aspects of each of the + existing protocols remain unchanged. Routing, address spaces, TCP + congestion control, etc. behave as specified in the extant standards. + Adapting to new standards and experimental algorithm heuristics for + improving performance will become much easier once the move to BLOAT + has been completed. + +1.4. Requirement Levels + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in BCP 14, RFC 2119 + [RFC2119]. + +2. IPoXML + + This protocol MUST be implemented to be compliant with this RFC. + IPoXML is the root protocol REQUIRED for effective use of TCPoXML + (section 3.) and higher-level application protocols. + + The DTD for this document type can be found in section 7.1. + + The routing of IPoXML can be easily implemented on hosts with an XML + parser, as the regular structure lends itself handily to parsing and + validation of the document/datagram and then processing the + destination address, TTL, and checksum before sending it on to its + next-hop. + + The reformulation of IPv4 was chosen over IPv6 [RFC2460] due to the + wider deployment of IPv4 and the fact that implementing IPv6 as XML + would have exceeded the 1500 byte Ethernet MTU. + + + +Kennedy Informational [Page 2] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + All BLOAT implementations MUST use - and specify - the UTF-8 encoding + of RFC 2279 [RFC2279]. All BLOAT document/datagrams MUST be well- + formed and include the XMLDecl. + +2.1. IP Description + + A number of items have changed (for the better) from the original IP + specification. Bit-masks, where present have been converted into + human-readable values. IP addresses are listed in their dotted- + decimal notation [RFC1123]. Length and checksum values are present + as decimal integers. + + To calculate the length and checksum fields of the IP element, a + canonicalized form of the element MUST be used. The canonical form + SHALL have no whitespace (including newline characters) between + elements and only one space character between attributes. There + SHALL NOT be a space following the last attribute in an element. + + An iterative method SHOULD be used to calculate checksums, as the + length field will vary based on the size of the checksum. + + The payload element bears special attention. Due to the character + set restrictions of XML, the payload of IP datagrams (which MAY + contain arbitrary data) MUST be encoded for transport. This RFC + REQUIRES the contents of the payload to be encoded in the base-64 + encoding of RFC 2045 [RFC2045], but removes the requirement that the + encoded output MUST be wrapped on 76-character lines. + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 3] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +2.2. Example Datagram + + The following is an example IPoXML datagram with an empty payload: + + + + +
+ + + + + + + + + + + + + + + +
+ + +
+ +3. TCPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.2. + +3.1. TCP Description + + A number of items have changed from the original TCP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + To calculate the length and checksum fields of the TCP element, a + canonicalized form of the element MUST be used as in section 2.1. + + An iterative method SHOULD be used to calculate checksums as in + section 2.1. + + The payload element MUST be encoded as in section 2.1. + + + +Kennedy Informational [Page 4] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + The TCP offset element was expanded to a maximum of 255 from 16 to + allow for the increased size of the header in XML. + + TCPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +3.2. Example Datagram + + The following is an example TCPoXML datagram with an empty payload: + + + + + + + + + + + + + + + + + + + + + + + + +4. UDPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.3. + +4.1. UDP Description + + A number of items have changed from the original UDP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + + + + + + +Kennedy Informational [Page 5] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + To calculate the length and checksum fields of the UDP element, a + canonicalized form of the element MUST be used as in section 2.1. An + iterative method SHOULD be used to calculate checksums as in section + 2.1. + + The payload element MUST be encoded as in section 2.1. + + UDPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +4.2. Example Datagram + + The following is an example UDPoXML datagram with an empty payload: + + + + + + + + + + + + + + +5. Network Transport + + This document provides for the transmission of BLOAT datagrams over + two common families of physical layer transport. Future RFCs will + address additional transports as routing vendors catch up to the + specification, and we begin to see BLOAT routed across the Internet + backbone. + +5.1. Ethernet + + BLOAT is encapsulated in Ethernet datagrams as in [RFC894] with the + exception that the type field of the Ethernet frame MUST contain the + value 0xBEEF. The first 5 octets of the Ethernet frame payload will + be 0x3c 3f 78 6d 6c (" + --> + + + + +Kennedy Informational [Page 7] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 9] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 10] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + +7.2. TCPoXML DTD + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 11] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 12] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + +7.3. UDPoXML DTD + + + + + + + + + + + + + + + +Kennedy Informational [Page 13] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +8. Security Considerations + + XML, as a subset of SGML, has the same security considerations as + specified in SGML Media Types [RFC1874]. Security considerations + that apply to IP, TCP and UDP also likely apply to BLOAT as it does + not attempt to correct for issues not related to message format. + +9. References + + [JABBER] Miller, J., "Jabber", draft-miller-jabber-00.txt, + February 2002. (Work in Progress) + + [RFC768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, + August 1980. + + [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791, + September 1981. + + [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC + 793, September 1981. + + [RFC894] Hornig, C., "Standard for the Transmission of IP + Datagrams over Ethernet Networks.", RFC 894, April 1984. + + [RFC1042] Postel, J. and J. Reynolds, "Standard for the + Transmission of IP Datagrams Over IEEE 802 Networks", STD + 43, RFC 1042, February 1988. + + [RFC1123] Braden, R., "Requirements for Internet Hosts - + Application and Support", RFC 1123, October 1989. + + [RFC1874] Levinson, E., "SGML Media Types", RFC 1874, December + 1995. + + [RFC2003] Perkins, C., "IP Encapsulation within IP", RFC 2003, + October 1996. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", RFC 2279, January 1998. + + + + + +Kennedy Informational [Page 14] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6 + (IPv6) Specification", RFC 2460, December 1998. + + [RFC3080] Rose, M., "The Blocks Extensible Exchange Protocol Core", + RFC 3080, March 2001. + + [SOAP] Box, D., Ehnebuske, D., Kakivaya, G., Layman, A., + Mendelsohn, N., Nielsen, H. F., Thatte, S. Winer, D., + "Simple Object Access Protocol (SOAP) 1.1" World Wide Web + Consortium Note, May 2000 http://www.w3.org/TR/SOAP/ + + [XML] Bray, T., Paoli, J., Sperberg-McQueen, C. M., "Extensible + Markup Language (XML)" World Wide Web Consortium + Recommendation REC- xml-19980210. + http://www.w3.org/TR/1998/REC-xml-19980210 + +10. Author's Address + + Hugh Kennedy + Mimezine + 1060 West Addison + Chicago, IL 60613 + USA + + EMail: kennedyh@engin.umich.edu + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 15] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +11. Full Copyright Statement + + Copyright (C) The Internet Society (2002). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 16] + + + + + + + +Network Working Group H. Kennedy +Request for Comments: 3252 Mimezine +Category: Informational 1 April 2002 + + + Binary Lexical Octet Ad-hoc Transport + +Status of this Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2002). All Rights Reserved. + +Abstract + + This document defines a reformulation of IP and two transport layer + protocols (TCP and UDP) as XML applications. + +1. Introduction + +1.1. Overview + + This document describes the Binary Lexical Octet Ad-hoc Transport + (BLOAT): a reformulation of a widely-deployed network-layer protocol + (IP [RFC791]), and two associated transport layer protocols (TCP + [RFC793] and UDP [RFC768]) as XML [XML] applications. It also + describes methods for transporting BLOAT over Ethernet and IEEE 802 + networks as well as encapsulating BLOAT in IP for gatewaying BLOAT + across the public Internet. + +1.2. Motivation + + The wild popularity of XML as a basis for application-level protocols + such as the Blocks Extensible Exchange Protocol [RFC3080], the Simple + Object Access Protocol [SOAP], and Jabber [JABBER] prompted + investigation into the possibility of extending the use of XML in the + protocol stack. Using XML at both the transport and network layer in + addition to the application layer would provide for an amazing amount + of power and flexibility while removing dependencies on proprietary + and hard-to-understand binary protocols. This protocol unification + would also allow applications to use a single XML parser for all + aspects of their operation, eliminating developer time spent figuring + out the intricacies of each new protocol, and moving the hard work of + + + + +Kennedy Informational [Page 1] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + parsing to the XML toolset. The use of XML also mitigates concerns + over "network vs. host" byte ordering which is at the root of many + network application bugs. + +1.3. Relation to Existing Protocols + + The reformulations specified in this RFC follow as closely as + possible the spirit of the RFCs on which they are based, and so MAY + contain elements or attributes that would not be needed in a pure + reworking (e.g. length attributes, which are implicit in XML.) + + The layering of network and transport protocols are maintained in + this RFC despite the optimizations that could be made if the line + were somewhat blurred (i.e. merging TCP and IP into a single, larger + element in the DTD) in order to foster future use of this protocol as + a basis for reformulating other protocols (such as ICMP.) + + Other than the encoding, the behavioral aspects of each of the + existing protocols remain unchanged. Routing, address spaces, TCP + congestion control, etc. behave as specified in the extant standards. + Adapting to new standards and experimental algorithm heuristics for + improving performance will become much easier once the move to BLOAT + has been completed. + +1.4. Requirement Levels + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in BCP 14, RFC 2119 + [RFC2119]. + +2. IPoXML + + This protocol MUST be implemented to be compliant with this RFC. + IPoXML is the root protocol REQUIRED for effective use of TCPoXML + (section 3.) and higher-level application protocols. + + The DTD for this document type can be found in section 7.1. + + The routing of IPoXML can be easily implemented on hosts with an XML + parser, as the regular structure lends itself handily to parsing and + validation of the document/datagram and then processing the + destination address, TTL, and checksum before sending it on to its + next-hop. + + The reformulation of IPv4 was chosen over IPv6 [RFC2460] due to the + wider deployment of IPv4 and the fact that implementing IPv6 as XML + would have exceeded the 1500 byte Ethernet MTU. + + + +Kennedy Informational [Page 2] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + All BLOAT implementations MUST use - and specify - the UTF-8 encoding + of RFC 2279 [RFC2279]. All BLOAT document/datagrams MUST be well- + formed and include the XMLDecl. + +2.1. IP Description + + A number of items have changed (for the better) from the original IP + specification. Bit-masks, where present have been converted into + human-readable values. IP addresses are listed in their dotted- + decimal notation [RFC1123]. Length and checksum values are present + as decimal integers. + + To calculate the length and checksum fields of the IP element, a + canonicalized form of the element MUST be used. The canonical form + SHALL have no whitespace (including newline characters) between + elements and only one space character between attributes. There + SHALL NOT be a space following the last attribute in an element. + + An iterative method SHOULD be used to calculate checksums, as the + length field will vary based on the size of the checksum. + + The payload element bears special attention. Due to the character + set restrictions of XML, the payload of IP datagrams (which MAY + contain arbitrary data) MUST be encoded for transport. This RFC + REQUIRES the contents of the payload to be encoded in the base-64 + encoding of RFC 2045 [RFC2045], but removes the requirement that the + encoded output MUST be wrapped on 76-character lines. + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 3] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +2.2. Example Datagram + + The following is an example IPoXML datagram with an empty payload: + + + + +
+ + + + + + + + + + + + + + + +
+ + +
+ +3. TCPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.2. + +3.1. TCP Description + + A number of items have changed from the original TCP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + To calculate the length and checksum fields of the TCP element, a + canonicalized form of the element MUST be used as in section 2.1. + + An iterative method SHOULD be used to calculate checksums as in + section 2.1. + + The payload element MUST be encoded as in section 2.1. + + + +Kennedy Informational [Page 4] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + The TCP offset element was expanded to a maximum of 255 from 16 to + allow for the increased size of the header in XML. + + TCPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +3.2. Example Datagram + + The following is an example TCPoXML datagram with an empty payload: + + + + + + + + + + + + + + + + + + + + + + + + +4. UDPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.3. + +4.1. UDP Description + + A number of items have changed from the original UDP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + + + + + + +Kennedy Informational [Page 5] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + To calculate the length and checksum fields of the UDP element, a + canonicalized form of the element MUST be used as in section 2.1. An + iterative method SHOULD be used to calculate checksums as in section + 2.1. + + The payload element MUST be encoded as in section 2.1. + + UDPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +4.2. Example Datagram + + The following is an example UDPoXML datagram with an empty payload: + + + + + + + + + + + + + + +5. Network Transport + + This document provides for the transmission of BLOAT datagrams over + two common families of physical layer transport. Future RFCs will + address additional transports as routing vendors catch up to the + specification, and we begin to see BLOAT routed across the Internet + backbone. + +5.1. Ethernet + + BLOAT is encapsulated in Ethernet datagrams as in [RFC894] with the + exception that the type field of the Ethernet frame MUST contain the + value 0xBEEF. The first 5 octets of the Ethernet frame payload will + be 0x3c 3f 78 6d 6c (" + --> + + + + +Kennedy Informational [Page 7] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 9] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 10] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + +7.2. TCPoXML DTD + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 11] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 12] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + +7.3. UDPoXML DTD + + + + + + + + + + + + + + + +Kennedy Informational [Page 13] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +8. Security Considerations + + XML, as a subset of SGML, has the same security considerations as + specified in SGML Media Types [RFC1874]. Security considerations + that apply to IP, TCP and UDP also likely apply to BLOAT as it does + not attempt to correct for issues not related to message format. + +9. References + + [JABBER] Miller, J., "Jabber", draft-miller-jabber-00.txt, + February 2002. (Work in Progress) + + [RFC768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, + August 1980. + + [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791, + September 1981. + + [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC + 793, September 1981. + + [RFC894] Hornig, C., "Standard for the Transmission of IP + Datagrams over Ethernet Networks.", RFC 894, April 1984. + + [RFC1042] Postel, J. and J. Reynolds, "Standard for the + Transmission of IP Datagrams Over IEEE 802 Networks", STD + 43, RFC 1042, February 1988. + + [RFC1123] Braden, R., "Requirements for Internet Hosts - + Application and Support", RFC 1123, October 1989. + + [RFC1874] Levinson, E., "SGML Media Types", RFC 1874, December + 1995. + + [RFC2003] Perkins, C., "IP Encapsulation within IP", RFC 2003, + October 1996. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", RFC 2279, January 1998. + + + + + +Kennedy Informational [Page 14] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6 + (IPv6) Specification", RFC 2460, December 1998. + + [RFC3080] Rose, M., "The Blocks Extensible Exchange Protocol Core", + RFC 3080, March 2001. + + [SOAP] Box, D., Ehnebuske, D., Kakivaya, G., Layman, A., + Mendelsohn, N., Nielsen, H. F., Thatte, S. Winer, D., + "Simple Object Access Protocol (SOAP) 1.1" World Wide Web + Consortium Note, May 2000 http://www.w3.org/TR/SOAP/ + + [XML] Bray, T., Paoli, J., Sperberg-McQueen, C. M., "Extensible + Markup Language (XML)" World Wide Web Consortium + Recommendation REC- xml-19980210. + http://www.w3.org/TR/1998/REC-xml-19980210 + +10. Author's Address + + Hugh Kennedy + Mimezine + 1060 West Addison + Chicago, IL 60613 + USA + + EMail: kennedyh@engin.umich.edu + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 15] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +11. Full Copyright Statement + + Copyright (C) The Internet Society (2002). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 16] + + + + + + + +Network Working Group H. Kennedy +Request for Comments: 3252 Mimezine +Category: Informational 1 April 2002 + + + Binary Lexical Octet Ad-hoc Transport + +Status of this Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2002). All Rights Reserved. + +Abstract + + This document defines a reformulation of IP and two transport layer + protocols (TCP and UDP) as XML applications. + +1. Introduction + +1.1. Overview + + This document describes the Binary Lexical Octet Ad-hoc Transport + (BLOAT): a reformulation of a widely-deployed network-layer protocol + (IP [RFC791]), and two associated transport layer protocols (TCP + [RFC793] and UDP [RFC768]) as XML [XML] applications. It also + describes methods for transporting BLOAT over Ethernet and IEEE 802 + networks as well as encapsulating BLOAT in IP for gatewaying BLOAT + across the public Internet. + +1.2. Motivation + + The wild popularity of XML as a basis for application-level protocols + such as the Blocks Extensible Exchange Protocol [RFC3080], the Simple + Object Access Protocol [SOAP], and Jabber [JABBER] prompted + investigation into the possibility of extending the use of XML in the + protocol stack. Using XML at both the transport and network layer in + addition to the application layer would provide for an amazing amount + of power and flexibility while removing dependencies on proprietary + and hard-to-understand binary protocols. This protocol unification + would also allow applications to use a single XML parser for all + aspects of their operation, eliminating developer time spent figuring + out the intricacies of each new protocol, and moving the hard work of + + + + +Kennedy Informational [Page 1] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + parsing to the XML toolset. The use of XML also mitigates concerns + over "network vs. host" byte ordering which is at the root of many + network application bugs. + +1.3. Relation to Existing Protocols + + The reformulations specified in this RFC follow as closely as + possible the spirit of the RFCs on which they are based, and so MAY + contain elements or attributes that would not be needed in a pure + reworking (e.g. length attributes, which are implicit in XML.) + + The layering of network and transport protocols are maintained in + this RFC despite the optimizations that could be made if the line + were somewhat blurred (i.e. merging TCP and IP into a single, larger + element in the DTD) in order to foster future use of this protocol as + a basis for reformulating other protocols (such as ICMP.) + + Other than the encoding, the behavioral aspects of each of the + existing protocols remain unchanged. Routing, address spaces, TCP + congestion control, etc. behave as specified in the extant standards. + Adapting to new standards and experimental algorithm heuristics for + improving performance will become much easier once the move to BLOAT + has been completed. + +1.4. Requirement Levels + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in BCP 14, RFC 2119 + [RFC2119]. + +2. IPoXML + + This protocol MUST be implemented to be compliant with this RFC. + IPoXML is the root protocol REQUIRED for effective use of TCPoXML + (section 3.) and higher-level application protocols. + + The DTD for this document type can be found in section 7.1. + + The routing of IPoXML can be easily implemented on hosts with an XML + parser, as the regular structure lends itself handily to parsing and + validation of the document/datagram and then processing the + destination address, TTL, and checksum before sending it on to its + next-hop. + + The reformulation of IPv4 was chosen over IPv6 [RFC2460] due to the + wider deployment of IPv4 and the fact that implementing IPv6 as XML + would have exceeded the 1500 byte Ethernet MTU. + + + +Kennedy Informational [Page 2] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + All BLOAT implementations MUST use - and specify - the UTF-8 encoding + of RFC 2279 [RFC2279]. All BLOAT document/datagrams MUST be well- + formed and include the XMLDecl. + +2.1. IP Description + + A number of items have changed (for the better) from the original IP + specification. Bit-masks, where present have been converted into + human-readable values. IP addresses are listed in their dotted- + decimal notation [RFC1123]. Length and checksum values are present + as decimal integers. + + To calculate the length and checksum fields of the IP element, a + canonicalized form of the element MUST be used. The canonical form + SHALL have no whitespace (including newline characters) between + elements and only one space character between attributes. There + SHALL NOT be a space following the last attribute in an element. + + An iterative method SHOULD be used to calculate checksums, as the + length field will vary based on the size of the checksum. + + The payload element bears special attention. Due to the character + set restrictions of XML, the payload of IP datagrams (which MAY + contain arbitrary data) MUST be encoded for transport. This RFC + REQUIRES the contents of the payload to be encoded in the base-64 + encoding of RFC 2045 [RFC2045], but removes the requirement that the + encoded output MUST be wrapped on 76-character lines. + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 3] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +2.2. Example Datagram + + The following is an example IPoXML datagram with an empty payload: + + + + +
+ + + + + + + + + + + + + + + +
+ + +
+ +3. TCPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.2. + +3.1. TCP Description + + A number of items have changed from the original TCP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + To calculate the length and checksum fields of the TCP element, a + canonicalized form of the element MUST be used as in section 2.1. + + An iterative method SHOULD be used to calculate checksums as in + section 2.1. + + The payload element MUST be encoded as in section 2.1. + + + +Kennedy Informational [Page 4] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + The TCP offset element was expanded to a maximum of 255 from 16 to + allow for the increased size of the header in XML. + + TCPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +3.2. Example Datagram + + The following is an example TCPoXML datagram with an empty payload: + + + + + + + + + + + + + + + + + + + + + + + + +4. UDPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.3. + +4.1. UDP Description + + A number of items have changed from the original UDP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + + + + + + +Kennedy Informational [Page 5] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + To calculate the length and checksum fields of the UDP element, a + canonicalized form of the element MUST be used as in section 2.1. An + iterative method SHOULD be used to calculate checksums as in section + 2.1. + + The payload element MUST be encoded as in section 2.1. + + UDPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +4.2. Example Datagram + + The following is an example UDPoXML datagram with an empty payload: + + + + + + + + + + + + + + +5. Network Transport + + This document provides for the transmission of BLOAT datagrams over + two common families of physical layer transport. Future RFCs will + address additional transports as routing vendors catch up to the + specification, and we begin to see BLOAT routed across the Internet + backbone. + +5.1. Ethernet + + BLOAT is encapsulated in Ethernet datagrams as in [RFC894] with the + exception that the type field of the Ethernet frame MUST contain the + value 0xBEEF. The first 5 octets of the Ethernet frame payload will + be 0x3c 3f 78 6d 6c (" + --> + + + + +Kennedy Informational [Page 7] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 9] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 10] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + +7.2. TCPoXML DTD + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 11] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 12] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + +7.3. UDPoXML DTD + + + + + + + + + + + + + + + +Kennedy Informational [Page 13] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +8. Security Considerations + + XML, as a subset of SGML, has the same security considerations as + specified in SGML Media Types [RFC1874]. Security considerations + that apply to IP, TCP and UDP also likely apply to BLOAT as it does + not attempt to correct for issues not related to message format. + +9. References + + [JABBER] Miller, J., "Jabber", draft-miller-jabber-00.txt, + February 2002. (Work in Progress) + + [RFC768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, + August 1980. + + [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791, + September 1981. + + [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC + 793, September 1981. + + [RFC894] Hornig, C., "Standard for the Transmission of IP + Datagrams over Ethernet Networks.", RFC 894, April 1984. + + [RFC1042] Postel, J. and J. Reynolds, "Standard for the + Transmission of IP Datagrams Over IEEE 802 Networks", STD + 43, RFC 1042, February 1988. + + [RFC1123] Braden, R., "Requirements for Internet Hosts - + Application and Support", RFC 1123, October 1989. + + [RFC1874] Levinson, E., "SGML Media Types", RFC 1874, December + 1995. + + [RFC2003] Perkins, C., "IP Encapsulation within IP", RFC 2003, + October 1996. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", RFC 2279, January 1998. + + + + + +Kennedy Informational [Page 14] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6 + (IPv6) Specification", RFC 2460, December 1998. + + [RFC3080] Rose, M., "The Blocks Extensible Exchange Protocol Core", + RFC 3080, March 2001. + + [SOAP] Box, D., Ehnebuske, D., Kakivaya, G., Layman, A., + Mendelsohn, N., Nielsen, H. F., Thatte, S. Winer, D., + "Simple Object Access Protocol (SOAP) 1.1" World Wide Web + Consortium Note, May 2000 http://www.w3.org/TR/SOAP/ + + [XML] Bray, T., Paoli, J., Sperberg-McQueen, C. M., "Extensible + Markup Language (XML)" World Wide Web Consortium + Recommendation REC- xml-19980210. + http://www.w3.org/TR/1998/REC-xml-19980210 + +10. Author's Address + + Hugh Kennedy + Mimezine + 1060 West Addison + Chicago, IL 60613 + USA + + EMail: kennedyh@engin.umich.edu + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 15] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +11. Full Copyright Statement + + Copyright (C) The Internet Society (2002). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 16] + + + + + + + +Network Working Group H. Kennedy +Request for Comments: 3252 Mimezine +Category: Informational 1 April 2002 + + + Binary Lexical Octet Ad-hoc Transport + +Status of this Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2002). All Rights Reserved. + +Abstract + + This document defines a reformulation of IP and two transport layer + protocols (TCP and UDP) as XML applications. + +1. Introduction + +1.1. Overview + + This document describes the Binary Lexical Octet Ad-hoc Transport + (BLOAT): a reformulation of a widely-deployed network-layer protocol + (IP [RFC791]), and two associated transport layer protocols (TCP + [RFC793] and UDP [RFC768]) as XML [XML] applications. It also + describes methods for transporting BLOAT over Ethernet and IEEE 802 + networks as well as encapsulating BLOAT in IP for gatewaying BLOAT + across the public Internet. + +1.2. Motivation + + The wild popularity of XML as a basis for application-level protocols + such as the Blocks Extensible Exchange Protocol [RFC3080], the Simple + Object Access Protocol [SOAP], and Jabber [JABBER] prompted + investigation into the possibility of extending the use of XML in the + protocol stack. Using XML at both the transport and network layer in + addition to the application layer would provide for an amazing amount + of power and flexibility while removing dependencies on proprietary + and hard-to-understand binary protocols. This protocol unification + would also allow applications to use a single XML parser for all + aspects of their operation, eliminating developer time spent figuring + out the intricacies of each new protocol, and moving the hard work of + + + + +Kennedy Informational [Page 1] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + parsing to the XML toolset. The use of XML also mitigates concerns + over "network vs. host" byte ordering which is at the root of many + network application bugs. + +1.3. Relation to Existing Protocols + + The reformulations specified in this RFC follow as closely as + possible the spirit of the RFCs on which they are based, and so MAY + contain elements or attributes that would not be needed in a pure + reworking (e.g. length attributes, which are implicit in XML.) + + The layering of network and transport protocols are maintained in + this RFC despite the optimizations that could be made if the line + were somewhat blurred (i.e. merging TCP and IP into a single, larger + element in the DTD) in order to foster future use of this protocol as + a basis for reformulating other protocols (such as ICMP.) + + Other than the encoding, the behavioral aspects of each of the + existing protocols remain unchanged. Routing, address spaces, TCP + congestion control, etc. behave as specified in the extant standards. + Adapting to new standards and experimental algorithm heuristics for + improving performance will become much easier once the move to BLOAT + has been completed. + +1.4. Requirement Levels + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in BCP 14, RFC 2119 + [RFC2119]. + +2. IPoXML + + This protocol MUST be implemented to be compliant with this RFC. + IPoXML is the root protocol REQUIRED for effective use of TCPoXML + (section 3.) and higher-level application protocols. + + The DTD for this document type can be found in section 7.1. + + The routing of IPoXML can be easily implemented on hosts with an XML + parser, as the regular structure lends itself handily to parsing and + validation of the document/datagram and then processing the + destination address, TTL, and checksum before sending it on to its + next-hop. + + The reformulation of IPv4 was chosen over IPv6 [RFC2460] due to the + wider deployment of IPv4 and the fact that implementing IPv6 as XML + would have exceeded the 1500 byte Ethernet MTU. + + + +Kennedy Informational [Page 2] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + All BLOAT implementations MUST use - and specify - the UTF-8 encoding + of RFC 2279 [RFC2279]. All BLOAT document/datagrams MUST be well- + formed and include the XMLDecl. + +2.1. IP Description + + A number of items have changed (for the better) from the original IP + specification. Bit-masks, where present have been converted into + human-readable values. IP addresses are listed in their dotted- + decimal notation [RFC1123]. Length and checksum values are present + as decimal integers. + + To calculate the length and checksum fields of the IP element, a + canonicalized form of the element MUST be used. The canonical form + SHALL have no whitespace (including newline characters) between + elements and only one space character between attributes. There + SHALL NOT be a space following the last attribute in an element. + + An iterative method SHOULD be used to calculate checksums, as the + length field will vary based on the size of the checksum. + + The payload element bears special attention. Due to the character + set restrictions of XML, the payload of IP datagrams (which MAY + contain arbitrary data) MUST be encoded for transport. This RFC + REQUIRES the contents of the payload to be encoded in the base-64 + encoding of RFC 2045 [RFC2045], but removes the requirement that the + encoded output MUST be wrapped on 76-character lines. + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 3] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +2.2. Example Datagram + + The following is an example IPoXML datagram with an empty payload: + + + + +
+ + + + + + + + + + + + + + + +
+ + +
+ +3. TCPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.2. + +3.1. TCP Description + + A number of items have changed from the original TCP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + To calculate the length and checksum fields of the TCP element, a + canonicalized form of the element MUST be used as in section 2.1. + + An iterative method SHOULD be used to calculate checksums as in + section 2.1. + + The payload element MUST be encoded as in section 2.1. + + + +Kennedy Informational [Page 4] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + The TCP offset element was expanded to a maximum of 255 from 16 to + allow for the increased size of the header in XML. + + TCPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +3.2. Example Datagram + + The following is an example TCPoXML datagram with an empty payload: + + + + + + + + + + + + + + + + + + + + + + + + +4. UDPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.3. + +4.1. UDP Description + + A number of items have changed from the original UDP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + + + + + + +Kennedy Informational [Page 5] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + To calculate the length and checksum fields of the UDP element, a + canonicalized form of the element MUST be used as in section 2.1. An + iterative method SHOULD be used to calculate checksums as in section + 2.1. + + The payload element MUST be encoded as in section 2.1. + + UDPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +4.2. Example Datagram + + The following is an example UDPoXML datagram with an empty payload: + + + + + + + + + + + + + + +5. Network Transport + + This document provides for the transmission of BLOAT datagrams over + two common families of physical layer transport. Future RFCs will + address additional transports as routing vendors catch up to the + specification, and we begin to see BLOAT routed across the Internet + backbone. + +5.1. Ethernet + + BLOAT is encapsulated in Ethernet datagrams as in [RFC894] with the + exception that the type field of the Ethernet frame MUST contain the + value 0xBEEF. The first 5 octets of the Ethernet frame payload will + be 0x3c 3f 78 6d 6c (" + --> + + + + +Kennedy Informational [Page 7] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 9] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 10] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + +7.2. TCPoXML DTD + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 11] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 12] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + +7.3. UDPoXML DTD + + + + + + + + + + + + + + + +Kennedy Informational [Page 13] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +8. Security Considerations + + XML, as a subset of SGML, has the same security considerations as + specified in SGML Media Types [RFC1874]. Security considerations + that apply to IP, TCP and UDP also likely apply to BLOAT as it does + not attempt to correct for issues not related to message format. + +9. References + + [JABBER] Miller, J., "Jabber", draft-miller-jabber-00.txt, + February 2002. (Work in Progress) + + [RFC768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, + August 1980. + + [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791, + September 1981. + + [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC + 793, September 1981. + + [RFC894] Hornig, C., "Standard for the Transmission of IP + Datagrams over Ethernet Networks.", RFC 894, April 1984. + + [RFC1042] Postel, J. and J. Reynolds, "Standard for the + Transmission of IP Datagrams Over IEEE 802 Networks", STD + 43, RFC 1042, February 1988. + + [RFC1123] Braden, R., "Requirements for Internet Hosts - + Application and Support", RFC 1123, October 1989. + + [RFC1874] Levinson, E., "SGML Media Types", RFC 1874, December + 1995. + + [RFC2003] Perkins, C., "IP Encapsulation within IP", RFC 2003, + October 1996. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", RFC 2279, January 1998. + + + + + +Kennedy Informational [Page 14] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6 + (IPv6) Specification", RFC 2460, December 1998. + + [RFC3080] Rose, M., "The Blocks Extensible Exchange Protocol Core", + RFC 3080, March 2001. + + [SOAP] Box, D., Ehnebuske, D., Kakivaya, G., Layman, A., + Mendelsohn, N., Nielsen, H. F., Thatte, S. Winer, D., + "Simple Object Access Protocol (SOAP) 1.1" World Wide Web + Consortium Note, May 2000 http://www.w3.org/TR/SOAP/ + + [XML] Bray, T., Paoli, J., Sperberg-McQueen, C. M., "Extensible + Markup Language (XML)" World Wide Web Consortium + Recommendation REC- xml-19980210. + http://www.w3.org/TR/1998/REC-xml-19980210 + +10. Author's Address + + Hugh Kennedy + Mimezine + 1060 West Addison + Chicago, IL 60613 + USA + + EMail: kennedyh@engin.umich.edu + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 15] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +11. Full Copyright Statement + + Copyright (C) The Internet Society (2002). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 16] + + + + + + + +Network Working Group H. Kennedy +Request for Comments: 3252 Mimezine +Category: Informational 1 April 2002 + + + Binary Lexical Octet Ad-hoc Transport + +Status of this Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2002). All Rights Reserved. + +Abstract + + This document defines a reformulation of IP and two transport layer + protocols (TCP and UDP) as XML applications. + +1. Introduction + +1.1. Overview + + This document describes the Binary Lexical Octet Ad-hoc Transport + (BLOAT): a reformulation of a widely-deployed network-layer protocol + (IP [RFC791]), and two associated transport layer protocols (TCP + [RFC793] and UDP [RFC768]) as XML [XML] applications. It also + describes methods for transporting BLOAT over Ethernet and IEEE 802 + networks as well as encapsulating BLOAT in IP for gatewaying BLOAT + across the public Internet. + +1.2. Motivation + + The wild popularity of XML as a basis for application-level protocols + such as the Blocks Extensible Exchange Protocol [RFC3080], the Simple + Object Access Protocol [SOAP], and Jabber [JABBER] prompted + investigation into the possibility of extending the use of XML in the + protocol stack. Using XML at both the transport and network layer in + addition to the application layer would provide for an amazing amount + of power and flexibility while removing dependencies on proprietary + and hard-to-understand binary protocols. This protocol unification + would also allow applications to use a single XML parser for all + aspects of their operation, eliminating developer time spent figuring + out the intricacies of each new protocol, and moving the hard work of + + + + +Kennedy Informational [Page 1] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + parsing to the XML toolset. The use of XML also mitigates concerns + over "network vs. host" byte ordering which is at the root of many + network application bugs. + +1.3. Relation to Existing Protocols + + The reformulations specified in this RFC follow as closely as + possible the spirit of the RFCs on which they are based, and so MAY + contain elements or attributes that would not be needed in a pure + reworking (e.g. length attributes, which are implicit in XML.) + + The layering of network and transport protocols are maintained in + this RFC despite the optimizations that could be made if the line + were somewhat blurred (i.e. merging TCP and IP into a single, larger + element in the DTD) in order to foster future use of this protocol as + a basis for reformulating other protocols (such as ICMP.) + + Other than the encoding, the behavioral aspects of each of the + existing protocols remain unchanged. Routing, address spaces, TCP + congestion control, etc. behave as specified in the extant standards. + Adapting to new standards and experimental algorithm heuristics for + improving performance will become much easier once the move to BLOAT + has been completed. + +1.4. Requirement Levels + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in BCP 14, RFC 2119 + [RFC2119]. + +2. IPoXML + + This protocol MUST be implemented to be compliant with this RFC. + IPoXML is the root protocol REQUIRED for effective use of TCPoXML + (section 3.) and higher-level application protocols. + + The DTD for this document type can be found in section 7.1. + + The routing of IPoXML can be easily implemented on hosts with an XML + parser, as the regular structure lends itself handily to parsing and + validation of the document/datagram and then processing the + destination address, TTL, and checksum before sending it on to its + next-hop. + + The reformulation of IPv4 was chosen over IPv6 [RFC2460] due to the + wider deployment of IPv4 and the fact that implementing IPv6 as XML + would have exceeded the 1500 byte Ethernet MTU. + + + +Kennedy Informational [Page 2] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + All BLOAT implementations MUST use - and specify - the UTF-8 encoding + of RFC 2279 [RFC2279]. All BLOAT document/datagrams MUST be well- + formed and include the XMLDecl. + +2.1. IP Description + + A number of items have changed (for the better) from the original IP + specification. Bit-masks, where present have been converted into + human-readable values. IP addresses are listed in their dotted- + decimal notation [RFC1123]. Length and checksum values are present + as decimal integers. + + To calculate the length and checksum fields of the IP element, a + canonicalized form of the element MUST be used. The canonical form + SHALL have no whitespace (including newline characters) between + elements and only one space character between attributes. There + SHALL NOT be a space following the last attribute in an element. + + An iterative method SHOULD be used to calculate checksums, as the + length field will vary based on the size of the checksum. + + The payload element bears special attention. Due to the character + set restrictions of XML, the payload of IP datagrams (which MAY + contain arbitrary data) MUST be encoded for transport. This RFC + REQUIRES the contents of the payload to be encoded in the base-64 + encoding of RFC 2045 [RFC2045], but removes the requirement that the + encoded output MUST be wrapped on 76-character lines. + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 3] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +2.2. Example Datagram + + The following is an example IPoXML datagram with an empty payload: + + + + +
+ + + + + + + + + + + + + + + +
+ + +
+ +3. TCPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.2. + +3.1. TCP Description + + A number of items have changed from the original TCP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + To calculate the length and checksum fields of the TCP element, a + canonicalized form of the element MUST be used as in section 2.1. + + An iterative method SHOULD be used to calculate checksums as in + section 2.1. + + The payload element MUST be encoded as in section 2.1. + + + +Kennedy Informational [Page 4] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + The TCP offset element was expanded to a maximum of 255 from 16 to + allow for the increased size of the header in XML. + + TCPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +3.2. Example Datagram + + The following is an example TCPoXML datagram with an empty payload: + + + + + + + + + + + + + + + + + + + + + + + + +4. UDPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.3. + +4.1. UDP Description + + A number of items have changed from the original UDP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + + + + + + +Kennedy Informational [Page 5] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + To calculate the length and checksum fields of the UDP element, a + canonicalized form of the element MUST be used as in section 2.1. An + iterative method SHOULD be used to calculate checksums as in section + 2.1. + + The payload element MUST be encoded as in section 2.1. + + UDPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +4.2. Example Datagram + + The following is an example UDPoXML datagram with an empty payload: + + + + + + + + + + + + + + +5. Network Transport + + This document provides for the transmission of BLOAT datagrams over + two common families of physical layer transport. Future RFCs will + address additional transports as routing vendors catch up to the + specification, and we begin to see BLOAT routed across the Internet + backbone. + +5.1. Ethernet + + BLOAT is encapsulated in Ethernet datagrams as in [RFC894] with the + exception that the type field of the Ethernet frame MUST contain the + value 0xBEEF. The first 5 octets of the Ethernet frame payload will + be 0x3c 3f 78 6d 6c (" + --> + + + + +Kennedy Informational [Page 7] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 9] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 10] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + +7.2. TCPoXML DTD + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 11] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 12] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + +7.3. UDPoXML DTD + + + + + + + + + + + + + + + +Kennedy Informational [Page 13] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +8. Security Considerations + + XML, as a subset of SGML, has the same security considerations as + specified in SGML Media Types [RFC1874]. Security considerations + that apply to IP, TCP and UDP also likely apply to BLOAT as it does + not attempt to correct for issues not related to message format. + +9. References + + [JABBER] Miller, J., "Jabber", draft-miller-jabber-00.txt, + February 2002. (Work in Progress) + + [RFC768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, + August 1980. + + [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791, + September 1981. + + [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC + 793, September 1981. + + [RFC894] Hornig, C., "Standard for the Transmission of IP + Datagrams over Ethernet Networks.", RFC 894, April 1984. + + [RFC1042] Postel, J. and J. Reynolds, "Standard for the + Transmission of IP Datagrams Over IEEE 802 Networks", STD + 43, RFC 1042, February 1988. + + [RFC1123] Braden, R., "Requirements for Internet Hosts - + Application and Support", RFC 1123, October 1989. + + [RFC1874] Levinson, E., "SGML Media Types", RFC 1874, December + 1995. + + [RFC2003] Perkins, C., "IP Encapsulation within IP", RFC 2003, + October 1996. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", RFC 2279, January 1998. + + + + + +Kennedy Informational [Page 14] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6 + (IPv6) Specification", RFC 2460, December 1998. + + [RFC3080] Rose, M., "The Blocks Extensible Exchange Protocol Core", + RFC 3080, March 2001. + + [SOAP] Box, D., Ehnebuske, D., Kakivaya, G., Layman, A., + Mendelsohn, N., Nielsen, H. F., Thatte, S. Winer, D., + "Simple Object Access Protocol (SOAP) 1.1" World Wide Web + Consortium Note, May 2000 http://www.w3.org/TR/SOAP/ + + [XML] Bray, T., Paoli, J., Sperberg-McQueen, C. M., "Extensible + Markup Language (XML)" World Wide Web Consortium + Recommendation REC- xml-19980210. + http://www.w3.org/TR/1998/REC-xml-19980210 + +10. Author's Address + + Hugh Kennedy + Mimezine + 1060 West Addison + Chicago, IL 60613 + USA + + EMail: kennedyh@engin.umich.edu + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 15] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +11. Full Copyright Statement + + Copyright (C) The Internet Society (2002). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 16] + + + + + + + +Network Working Group H. Kennedy +Request for Comments: 3252 Mimezine +Category: Informational 1 April 2002 + + + Binary Lexical Octet Ad-hoc Transport + +Status of this Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2002). All Rights Reserved. + +Abstract + + This document defines a reformulation of IP and two transport layer + protocols (TCP and UDP) as XML applications. + +1. Introduction + +1.1. Overview + + This document describes the Binary Lexical Octet Ad-hoc Transport + (BLOAT): a reformulation of a widely-deployed network-layer protocol + (IP [RFC791]), and two associated transport layer protocols (TCP + [RFC793] and UDP [RFC768]) as XML [XML] applications. It also + describes methods for transporting BLOAT over Ethernet and IEEE 802 + networks as well as encapsulating BLOAT in IP for gatewaying BLOAT + across the public Internet. + +1.2. Motivation + + The wild popularity of XML as a basis for application-level protocols + such as the Blocks Extensible Exchange Protocol [RFC3080], the Simple + Object Access Protocol [SOAP], and Jabber [JABBER] prompted + investigation into the possibility of extending the use of XML in the + protocol stack. Using XML at both the transport and network layer in + addition to the application layer would provide for an amazing amount + of power and flexibility while removing dependencies on proprietary + and hard-to-understand binary protocols. This protocol unification + would also allow applications to use a single XML parser for all + aspects of their operation, eliminating developer time spent figuring + out the intricacies of each new protocol, and moving the hard work of + + + + +Kennedy Informational [Page 1] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + parsing to the XML toolset. The use of XML also mitigates concerns + over "network vs. host" byte ordering which is at the root of many + network application bugs. + +1.3. Relation to Existing Protocols + + The reformulations specified in this RFC follow as closely as + possible the spirit of the RFCs on which they are based, and so MAY + contain elements or attributes that would not be needed in a pure + reworking (e.g. length attributes, which are implicit in XML.) + + The layering of network and transport protocols are maintained in + this RFC despite the optimizations that could be made if the line + were somewhat blurred (i.e. merging TCP and IP into a single, larger + element in the DTD) in order to foster future use of this protocol as + a basis for reformulating other protocols (such as ICMP.) + + Other than the encoding, the behavioral aspects of each of the + existing protocols remain unchanged. Routing, address spaces, TCP + congestion control, etc. behave as specified in the extant standards. + Adapting to new standards and experimental algorithm heuristics for + improving performance will become much easier once the move to BLOAT + has been completed. + +1.4. Requirement Levels + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in BCP 14, RFC 2119 + [RFC2119]. + +2. IPoXML + + This protocol MUST be implemented to be compliant with this RFC. + IPoXML is the root protocol REQUIRED for effective use of TCPoXML + (section 3.) and higher-level application protocols. + + The DTD for this document type can be found in section 7.1. + + The routing of IPoXML can be easily implemented on hosts with an XML + parser, as the regular structure lends itself handily to parsing and + validation of the document/datagram and then processing the + destination address, TTL, and checksum before sending it on to its + next-hop. + + The reformulation of IPv4 was chosen over IPv6 [RFC2460] due to the + wider deployment of IPv4 and the fact that implementing IPv6 as XML + would have exceeded the 1500 byte Ethernet MTU. + + + +Kennedy Informational [Page 2] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + All BLOAT implementations MUST use - and specify - the UTF-8 encoding + of RFC 2279 [RFC2279]. All BLOAT document/datagrams MUST be well- + formed and include the XMLDecl. + +2.1. IP Description + + A number of items have changed (for the better) from the original IP + specification. Bit-masks, where present have been converted into + human-readable values. IP addresses are listed in their dotted- + decimal notation [RFC1123]. Length and checksum values are present + as decimal integers. + + To calculate the length and checksum fields of the IP element, a + canonicalized form of the element MUST be used. The canonical form + SHALL have no whitespace (including newline characters) between + elements and only one space character between attributes. There + SHALL NOT be a space following the last attribute in an element. + + An iterative method SHOULD be used to calculate checksums, as the + length field will vary based on the size of the checksum. + + The payload element bears special attention. Due to the character + set restrictions of XML, the payload of IP datagrams (which MAY + contain arbitrary data) MUST be encoded for transport. This RFC + REQUIRES the contents of the payload to be encoded in the base-64 + encoding of RFC 2045 [RFC2045], but removes the requirement that the + encoded output MUST be wrapped on 76-character lines. + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 3] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +2.2. Example Datagram + + The following is an example IPoXML datagram with an empty payload: + + + + +
+ + + + + + + + + + + + + + + +
+ + +
+ +3. TCPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.2. + +3.1. TCP Description + + A number of items have changed from the original TCP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + To calculate the length and checksum fields of the TCP element, a + canonicalized form of the element MUST be used as in section 2.1. + + An iterative method SHOULD be used to calculate checksums as in + section 2.1. + + The payload element MUST be encoded as in section 2.1. + + + +Kennedy Informational [Page 4] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + The TCP offset element was expanded to a maximum of 255 from 16 to + allow for the increased size of the header in XML. + + TCPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +3.2. Example Datagram + + The following is an example TCPoXML datagram with an empty payload: + + + + + + + + + + + + + + + + + + + + + + + + +4. UDPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.3. + +4.1. UDP Description + + A number of items have changed from the original UDP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + + + + + + +Kennedy Informational [Page 5] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + To calculate the length and checksum fields of the UDP element, a + canonicalized form of the element MUST be used as in section 2.1. An + iterative method SHOULD be used to calculate checksums as in section + 2.1. + + The payload element MUST be encoded as in section 2.1. + + UDPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +4.2. Example Datagram + + The following is an example UDPoXML datagram with an empty payload: + + + + + + + + + + + + + + +5. Network Transport + + This document provides for the transmission of BLOAT datagrams over + two common families of physical layer transport. Future RFCs will + address additional transports as routing vendors catch up to the + specification, and we begin to see BLOAT routed across the Internet + backbone. + +5.1. Ethernet + + BLOAT is encapsulated in Ethernet datagrams as in [RFC894] with the + exception that the type field of the Ethernet frame MUST contain the + value 0xBEEF. The first 5 octets of the Ethernet frame payload will + be 0x3c 3f 78 6d 6c (" + --> + + + + +Kennedy Informational [Page 7] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 9] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 10] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + +7.2. TCPoXML DTD + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 11] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 12] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + +7.3. UDPoXML DTD + + + + + + + + + + + + + + + +Kennedy Informational [Page 13] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +8. Security Considerations + + XML, as a subset of SGML, has the same security considerations as + specified in SGML Media Types [RFC1874]. Security considerations + that apply to IP, TCP and UDP also likely apply to BLOAT as it does + not attempt to correct for issues not related to message format. + +9. References + + [JABBER] Miller, J., "Jabber", draft-miller-jabber-00.txt, + February 2002. (Work in Progress) + + [RFC768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, + August 1980. + + [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791, + September 1981. + + [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC + 793, September 1981. + + [RFC894] Hornig, C., "Standard for the Transmission of IP + Datagrams over Ethernet Networks.", RFC 894, April 1984. + + [RFC1042] Postel, J. and J. Reynolds, "Standard for the + Transmission of IP Datagrams Over IEEE 802 Networks", STD + 43, RFC 1042, February 1988. + + [RFC1123] Braden, R., "Requirements for Internet Hosts - + Application and Support", RFC 1123, October 1989. + + [RFC1874] Levinson, E., "SGML Media Types", RFC 1874, December + 1995. + + [RFC2003] Perkins, C., "IP Encapsulation within IP", RFC 2003, + October 1996. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", RFC 2279, January 1998. + + + + + +Kennedy Informational [Page 14] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6 + (IPv6) Specification", RFC 2460, December 1998. + + [RFC3080] Rose, M., "The Blocks Extensible Exchange Protocol Core", + RFC 3080, March 2001. + + [SOAP] Box, D., Ehnebuske, D., Kakivaya, G., Layman, A., + Mendelsohn, N., Nielsen, H. F., Thatte, S. Winer, D., + "Simple Object Access Protocol (SOAP) 1.1" World Wide Web + Consortium Note, May 2000 http://www.w3.org/TR/SOAP/ + + [XML] Bray, T., Paoli, J., Sperberg-McQueen, C. M., "Extensible + Markup Language (XML)" World Wide Web Consortium + Recommendation REC- xml-19980210. + http://www.w3.org/TR/1998/REC-xml-19980210 + +10. Author's Address + + Hugh Kennedy + Mimezine + 1060 West Addison + Chicago, IL 60613 + USA + + EMail: kennedyh@engin.umich.edu + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 15] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +11. Full Copyright Statement + + Copyright (C) The Internet Society (2002). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 16] + + + + + + + +Network Working Group H. Kennedy +Request for Comments: 3252 Mimezine +Category: Informational 1 April 2002 + + + Binary Lexical Octet Ad-hoc Transport + +Status of this Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2002). All Rights Reserved. + +Abstract + + This document defines a reformulation of IP and two transport layer + protocols (TCP and UDP) as XML applications. + +1. Introduction + +1.1. Overview + + This document describes the Binary Lexical Octet Ad-hoc Transport + (BLOAT): a reformulation of a widely-deployed network-layer protocol + (IP [RFC791]), and two associated transport layer protocols (TCP + [RFC793] and UDP [RFC768]) as XML [XML] applications. It also + describes methods for transporting BLOAT over Ethernet and IEEE 802 + networks as well as encapsulating BLOAT in IP for gatewaying BLOAT + across the public Internet. + +1.2. Motivation + + The wild popularity of XML as a basis for application-level protocols + such as the Blocks Extensible Exchange Protocol [RFC3080], the Simple + Object Access Protocol [SOAP], and Jabber [JABBER] prompted + investigation into the possibility of extending the use of XML in the + protocol stack. Using XML at both the transport and network layer in + addition to the application layer would provide for an amazing amount + of power and flexibility while removing dependencies on proprietary + and hard-to-understand binary protocols. This protocol unification + would also allow applications to use a single XML parser for all + aspects of their operation, eliminating developer time spent figuring + out the intricacies of each new protocol, and moving the hard work of + + + + +Kennedy Informational [Page 1] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + parsing to the XML toolset. The use of XML also mitigates concerns + over "network vs. host" byte ordering which is at the root of many + network application bugs. + +1.3. Relation to Existing Protocols + + The reformulations specified in this RFC follow as closely as + possible the spirit of the RFCs on which they are based, and so MAY + contain elements or attributes that would not be needed in a pure + reworking (e.g. length attributes, which are implicit in XML.) + + The layering of network and transport protocols are maintained in + this RFC despite the optimizations that could be made if the line + were somewhat blurred (i.e. merging TCP and IP into a single, larger + element in the DTD) in order to foster future use of this protocol as + a basis for reformulating other protocols (such as ICMP.) + + Other than the encoding, the behavioral aspects of each of the + existing protocols remain unchanged. Routing, address spaces, TCP + congestion control, etc. behave as specified in the extant standards. + Adapting to new standards and experimental algorithm heuristics for + improving performance will become much easier once the move to BLOAT + has been completed. + +1.4. Requirement Levels + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in BCP 14, RFC 2119 + [RFC2119]. + +2. IPoXML + + This protocol MUST be implemented to be compliant with this RFC. + IPoXML is the root protocol REQUIRED for effective use of TCPoXML + (section 3.) and higher-level application protocols. + + The DTD for this document type can be found in section 7.1. + + The routing of IPoXML can be easily implemented on hosts with an XML + parser, as the regular structure lends itself handily to parsing and + validation of the document/datagram and then processing the + destination address, TTL, and checksum before sending it on to its + next-hop. + + The reformulation of IPv4 was chosen over IPv6 [RFC2460] due to the + wider deployment of IPv4 and the fact that implementing IPv6 as XML + would have exceeded the 1500 byte Ethernet MTU. + + + +Kennedy Informational [Page 2] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + All BLOAT implementations MUST use - and specify - the UTF-8 encoding + of RFC 2279 [RFC2279]. All BLOAT document/datagrams MUST be well- + formed and include the XMLDecl. + +2.1. IP Description + + A number of items have changed (for the better) from the original IP + specification. Bit-masks, where present have been converted into + human-readable values. IP addresses are listed in their dotted- + decimal notation [RFC1123]. Length and checksum values are present + as decimal integers. + + To calculate the length and checksum fields of the IP element, a + canonicalized form of the element MUST be used. The canonical form + SHALL have no whitespace (including newline characters) between + elements and only one space character between attributes. There + SHALL NOT be a space following the last attribute in an element. + + An iterative method SHOULD be used to calculate checksums, as the + length field will vary based on the size of the checksum. + + The payload element bears special attention. Due to the character + set restrictions of XML, the payload of IP datagrams (which MAY + contain arbitrary data) MUST be encoded for transport. This RFC + REQUIRES the contents of the payload to be encoded in the base-64 + encoding of RFC 2045 [RFC2045], but removes the requirement that the + encoded output MUST be wrapped on 76-character lines. + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 3] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +2.2. Example Datagram + + The following is an example IPoXML datagram with an empty payload: + + + + +
+ + + + + + + + + + + + + + + +
+ + +
+ +3. TCPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.2. + +3.1. TCP Description + + A number of items have changed from the original TCP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + To calculate the length and checksum fields of the TCP element, a + canonicalized form of the element MUST be used as in section 2.1. + + An iterative method SHOULD be used to calculate checksums as in + section 2.1. + + The payload element MUST be encoded as in section 2.1. + + + +Kennedy Informational [Page 4] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + The TCP offset element was expanded to a maximum of 255 from 16 to + allow for the increased size of the header in XML. + + TCPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +3.2. Example Datagram + + The following is an example TCPoXML datagram with an empty payload: + + + + + + + + + + + + + + + + + + + + + + + + +4. UDPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.3. + +4.1. UDP Description + + A number of items have changed from the original UDP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + + + + + + +Kennedy Informational [Page 5] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + To calculate the length and checksum fields of the UDP element, a + canonicalized form of the element MUST be used as in section 2.1. An + iterative method SHOULD be used to calculate checksums as in section + 2.1. + + The payload element MUST be encoded as in section 2.1. + + UDPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +4.2. Example Datagram + + The following is an example UDPoXML datagram with an empty payload: + + + + + + + + + + + + + + +5. Network Transport + + This document provides for the transmission of BLOAT datagrams over + two common families of physical layer transport. Future RFCs will + address additional transports as routing vendors catch up to the + specification, and we begin to see BLOAT routed across the Internet + backbone. + +5.1. Ethernet + + BLOAT is encapsulated in Ethernet datagrams as in [RFC894] with the + exception that the type field of the Ethernet frame MUST contain the + value 0xBEEF. The first 5 octets of the Ethernet frame payload will + be 0x3c 3f 78 6d 6c (" + --> + + + + +Kennedy Informational [Page 7] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 9] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 10] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + +7.2. TCPoXML DTD + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 11] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 12] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + +7.3. UDPoXML DTD + + + + + + + + + + + + + + + +Kennedy Informational [Page 13] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +8. Security Considerations + + XML, as a subset of SGML, has the same security considerations as + specified in SGML Media Types [RFC1874]. Security considerations + that apply to IP, TCP and UDP also likely apply to BLOAT as it does + not attempt to correct for issues not related to message format. + +9. References + + [JABBER] Miller, J., "Jabber", draft-miller-jabber-00.txt, + February 2002. (Work in Progress) + + [RFC768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, + August 1980. + + [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791, + September 1981. + + [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC + 793, September 1981. + + [RFC894] Hornig, C., "Standard for the Transmission of IP + Datagrams over Ethernet Networks.", RFC 894, April 1984. + + [RFC1042] Postel, J. and J. Reynolds, "Standard for the + Transmission of IP Datagrams Over IEEE 802 Networks", STD + 43, RFC 1042, February 1988. + + [RFC1123] Braden, R., "Requirements for Internet Hosts - + Application and Support", RFC 1123, October 1989. + + [RFC1874] Levinson, E., "SGML Media Types", RFC 1874, December + 1995. + + [RFC2003] Perkins, C., "IP Encapsulation within IP", RFC 2003, + October 1996. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", RFC 2279, January 1998. + + + + + +Kennedy Informational [Page 14] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6 + (IPv6) Specification", RFC 2460, December 1998. + + [RFC3080] Rose, M., "The Blocks Extensible Exchange Protocol Core", + RFC 3080, March 2001. + + [SOAP] Box, D., Ehnebuske, D., Kakivaya, G., Layman, A., + Mendelsohn, N., Nielsen, H. F., Thatte, S. Winer, D., + "Simple Object Access Protocol (SOAP) 1.1" World Wide Web + Consortium Note, May 2000 http://www.w3.org/TR/SOAP/ + + [XML] Bray, T., Paoli, J., Sperberg-McQueen, C. M., "Extensible + Markup Language (XML)" World Wide Web Consortium + Recommendation REC- xml-19980210. + http://www.w3.org/TR/1998/REC-xml-19980210 + +10. Author's Address + + Hugh Kennedy + Mimezine + 1060 West Addison + Chicago, IL 60613 + USA + + EMail: kennedyh@engin.umich.edu + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 15] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +11. Full Copyright Statement + + Copyright (C) The Internet Society (2002). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 16] + + + + + + + +Network Working Group H. Kennedy +Request for Comments: 3252 Mimezine +Category: Informational 1 April 2002 + + + Binary Lexical Octet Ad-hoc Transport + +Status of this Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2002). All Rights Reserved. + +Abstract + + This document defines a reformulation of IP and two transport layer + protocols (TCP and UDP) as XML applications. + +1. Introduction + +1.1. Overview + + This document describes the Binary Lexical Octet Ad-hoc Transport + (BLOAT): a reformulation of a widely-deployed network-layer protocol + (IP [RFC791]), and two associated transport layer protocols (TCP + [RFC793] and UDP [RFC768]) as XML [XML] applications. It also + describes methods for transporting BLOAT over Ethernet and IEEE 802 + networks as well as encapsulating BLOAT in IP for gatewaying BLOAT + across the public Internet. + +1.2. Motivation + + The wild popularity of XML as a basis for application-level protocols + such as the Blocks Extensible Exchange Protocol [RFC3080], the Simple + Object Access Protocol [SOAP], and Jabber [JABBER] prompted + investigation into the possibility of extending the use of XML in the + protocol stack. Using XML at both the transport and network layer in + addition to the application layer would provide for an amazing amount + of power and flexibility while removing dependencies on proprietary + and hard-to-understand binary protocols. This protocol unification + would also allow applications to use a single XML parser for all + aspects of their operation, eliminating developer time spent figuring + out the intricacies of each new protocol, and moving the hard work of + + + + +Kennedy Informational [Page 1] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + parsing to the XML toolset. The use of XML also mitigates concerns + over "network vs. host" byte ordering which is at the root of many + network application bugs. + +1.3. Relation to Existing Protocols + + The reformulations specified in this RFC follow as closely as + possible the spirit of the RFCs on which they are based, and so MAY + contain elements or attributes that would not be needed in a pure + reworking (e.g. length attributes, which are implicit in XML.) + + The layering of network and transport protocols are maintained in + this RFC despite the optimizations that could be made if the line + were somewhat blurred (i.e. merging TCP and IP into a single, larger + element in the DTD) in order to foster future use of this protocol as + a basis for reformulating other protocols (such as ICMP.) + + Other than the encoding, the behavioral aspects of each of the + existing protocols remain unchanged. Routing, address spaces, TCP + congestion control, etc. behave as specified in the extant standards. + Adapting to new standards and experimental algorithm heuristics for + improving performance will become much easier once the move to BLOAT + has been completed. + +1.4. Requirement Levels + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in BCP 14, RFC 2119 + [RFC2119]. + +2. IPoXML + + This protocol MUST be implemented to be compliant with this RFC. + IPoXML is the root protocol REQUIRED for effective use of TCPoXML + (section 3.) and higher-level application protocols. + + The DTD for this document type can be found in section 7.1. + + The routing of IPoXML can be easily implemented on hosts with an XML + parser, as the regular structure lends itself handily to parsing and + validation of the document/datagram and then processing the + destination address, TTL, and checksum before sending it on to its + next-hop. + + The reformulation of IPv4 was chosen over IPv6 [RFC2460] due to the + wider deployment of IPv4 and the fact that implementing IPv6 as XML + would have exceeded the 1500 byte Ethernet MTU. + + + +Kennedy Informational [Page 2] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + All BLOAT implementations MUST use - and specify - the UTF-8 encoding + of RFC 2279 [RFC2279]. All BLOAT document/datagrams MUST be well- + formed and include the XMLDecl. + +2.1. IP Description + + A number of items have changed (for the better) from the original IP + specification. Bit-masks, where present have been converted into + human-readable values. IP addresses are listed in their dotted- + decimal notation [RFC1123]. Length and checksum values are present + as decimal integers. + + To calculate the length and checksum fields of the IP element, a + canonicalized form of the element MUST be used. The canonical form + SHALL have no whitespace (including newline characters) between + elements and only one space character between attributes. There + SHALL NOT be a space following the last attribute in an element. + + An iterative method SHOULD be used to calculate checksums, as the + length field will vary based on the size of the checksum. + + The payload element bears special attention. Due to the character + set restrictions of XML, the payload of IP datagrams (which MAY + contain arbitrary data) MUST be encoded for transport. This RFC + REQUIRES the contents of the payload to be encoded in the base-64 + encoding of RFC 2045 [RFC2045], but removes the requirement that the + encoded output MUST be wrapped on 76-character lines. + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 3] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +2.2. Example Datagram + + The following is an example IPoXML datagram with an empty payload: + + + + +
+ + + + + + + + + + + + + + + +
+ + +
+ +3. TCPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.2. + +3.1. TCP Description + + A number of items have changed from the original TCP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + To calculate the length and checksum fields of the TCP element, a + canonicalized form of the element MUST be used as in section 2.1. + + An iterative method SHOULD be used to calculate checksums as in + section 2.1. + + The payload element MUST be encoded as in section 2.1. + + + +Kennedy Informational [Page 4] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + The TCP offset element was expanded to a maximum of 255 from 16 to + allow for the increased size of the header in XML. + + TCPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +3.2. Example Datagram + + The following is an example TCPoXML datagram with an empty payload: + + + + + + + + + + + + + + + + + + + + + + + + +4. UDPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.3. + +4.1. UDP Description + + A number of items have changed from the original UDP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + + + + + + +Kennedy Informational [Page 5] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + To calculate the length and checksum fields of the UDP element, a + canonicalized form of the element MUST be used as in section 2.1. An + iterative method SHOULD be used to calculate checksums as in section + 2.1. + + The payload element MUST be encoded as in section 2.1. + + UDPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +4.2. Example Datagram + + The following is an example UDPoXML datagram with an empty payload: + + + + + + + + + + + + + + +5. Network Transport + + This document provides for the transmission of BLOAT datagrams over + two common families of physical layer transport. Future RFCs will + address additional transports as routing vendors catch up to the + specification, and we begin to see BLOAT routed across the Internet + backbone. + +5.1. Ethernet + + BLOAT is encapsulated in Ethernet datagrams as in [RFC894] with the + exception that the type field of the Ethernet frame MUST contain the + value 0xBEEF. The first 5 octets of the Ethernet frame payload will + be 0x3c 3f 78 6d 6c (" + --> + + + + +Kennedy Informational [Page 7] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 9] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 10] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + +7.2. TCPoXML DTD + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 11] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 12] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + +7.3. UDPoXML DTD + + + + + + + + + + + + + + + +Kennedy Informational [Page 13] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +8. Security Considerations + + XML, as a subset of SGML, has the same security considerations as + specified in SGML Media Types [RFC1874]. Security considerations + that apply to IP, TCP and UDP also likely apply to BLOAT as it does + not attempt to correct for issues not related to message format. + +9. References + + [JABBER] Miller, J., "Jabber", draft-miller-jabber-00.txt, + February 2002. (Work in Progress) + + [RFC768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, + August 1980. + + [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791, + September 1981. + + [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC + 793, September 1981. + + [RFC894] Hornig, C., "Standard for the Transmission of IP + Datagrams over Ethernet Networks.", RFC 894, April 1984. + + [RFC1042] Postel, J. and J. Reynolds, "Standard for the + Transmission of IP Datagrams Over IEEE 802 Networks", STD + 43, RFC 1042, February 1988. + + [RFC1123] Braden, R., "Requirements for Internet Hosts - + Application and Support", RFC 1123, October 1989. + + [RFC1874] Levinson, E., "SGML Media Types", RFC 1874, December + 1995. + + [RFC2003] Perkins, C., "IP Encapsulation within IP", RFC 2003, + October 1996. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", RFC 2279, January 1998. + + + + + +Kennedy Informational [Page 14] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6 + (IPv6) Specification", RFC 2460, December 1998. + + [RFC3080] Rose, M., "The Blocks Extensible Exchange Protocol Core", + RFC 3080, March 2001. + + [SOAP] Box, D., Ehnebuske, D., Kakivaya, G., Layman, A., + Mendelsohn, N., Nielsen, H. F., Thatte, S. Winer, D., + "Simple Object Access Protocol (SOAP) 1.1" World Wide Web + Consortium Note, May 2000 http://www.w3.org/TR/SOAP/ + + [XML] Bray, T., Paoli, J., Sperberg-McQueen, C. M., "Extensible + Markup Language (XML)" World Wide Web Consortium + Recommendation REC- xml-19980210. + http://www.w3.org/TR/1998/REC-xml-19980210 + +10. Author's Address + + Hugh Kennedy + Mimezine + 1060 West Addison + Chicago, IL 60613 + USA + + EMail: kennedyh@engin.umich.edu + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 15] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +11. Full Copyright Statement + + Copyright (C) The Internet Society (2002). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 16] + + + + + + + +Network Working Group H. Kennedy +Request for Comments: 3252 Mimezine +Category: Informational 1 April 2002 + + + Binary Lexical Octet Ad-hoc Transport + +Status of this Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2002). All Rights Reserved. + +Abstract + + This document defines a reformulation of IP and two transport layer + protocols (TCP and UDP) as XML applications. + +1. Introduction + +1.1. Overview + + This document describes the Binary Lexical Octet Ad-hoc Transport + (BLOAT): a reformulation of a widely-deployed network-layer protocol + (IP [RFC791]), and two associated transport layer protocols (TCP + [RFC793] and UDP [RFC768]) as XML [XML] applications. It also + describes methods for transporting BLOAT over Ethernet and IEEE 802 + networks as well as encapsulating BLOAT in IP for gatewaying BLOAT + across the public Internet. + +1.2. Motivation + + The wild popularity of XML as a basis for application-level protocols + such as the Blocks Extensible Exchange Protocol [RFC3080], the Simple + Object Access Protocol [SOAP], and Jabber [JABBER] prompted + investigation into the possibility of extending the use of XML in the + protocol stack. Using XML at both the transport and network layer in + addition to the application layer would provide for an amazing amount + of power and flexibility while removing dependencies on proprietary + and hard-to-understand binary protocols. This protocol unification + would also allow applications to use a single XML parser for all + aspects of their operation, eliminating developer time spent figuring + out the intricacies of each new protocol, and moving the hard work of + + + + +Kennedy Informational [Page 1] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + parsing to the XML toolset. The use of XML also mitigates concerns + over "network vs. host" byte ordering which is at the root of many + network application bugs. + +1.3. Relation to Existing Protocols + + The reformulations specified in this RFC follow as closely as + possible the spirit of the RFCs on which they are based, and so MAY + contain elements or attributes that would not be needed in a pure + reworking (e.g. length attributes, which are implicit in XML.) + + The layering of network and transport protocols are maintained in + this RFC despite the optimizations that could be made if the line + were somewhat blurred (i.e. merging TCP and IP into a single, larger + element in the DTD) in order to foster future use of this protocol as + a basis for reformulating other protocols (such as ICMP.) + + Other than the encoding, the behavioral aspects of each of the + existing protocols remain unchanged. Routing, address spaces, TCP + congestion control, etc. behave as specified in the extant standards. + Adapting to new standards and experimental algorithm heuristics for + improving performance will become much easier once the move to BLOAT + has been completed. + +1.4. Requirement Levels + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in BCP 14, RFC 2119 + [RFC2119]. + +2. IPoXML + + This protocol MUST be implemented to be compliant with this RFC. + IPoXML is the root protocol REQUIRED for effective use of TCPoXML + (section 3.) and higher-level application protocols. + + The DTD for this document type can be found in section 7.1. + + The routing of IPoXML can be easily implemented on hosts with an XML + parser, as the regular structure lends itself handily to parsing and + validation of the document/datagram and then processing the + destination address, TTL, and checksum before sending it on to its + next-hop. + + The reformulation of IPv4 was chosen over IPv6 [RFC2460] due to the + wider deployment of IPv4 and the fact that implementing IPv6 as XML + would have exceeded the 1500 byte Ethernet MTU. + + + +Kennedy Informational [Page 2] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + All BLOAT implementations MUST use - and specify - the UTF-8 encoding + of RFC 2279 [RFC2279]. All BLOAT document/datagrams MUST be well- + formed and include the XMLDecl. + +2.1. IP Description + + A number of items have changed (for the better) from the original IP + specification. Bit-masks, where present have been converted into + human-readable values. IP addresses are listed in their dotted- + decimal notation [RFC1123]. Length and checksum values are present + as decimal integers. + + To calculate the length and checksum fields of the IP element, a + canonicalized form of the element MUST be used. The canonical form + SHALL have no whitespace (including newline characters) between + elements and only one space character between attributes. There + SHALL NOT be a space following the last attribute in an element. + + An iterative method SHOULD be used to calculate checksums, as the + length field will vary based on the size of the checksum. + + The payload element bears special attention. Due to the character + set restrictions of XML, the payload of IP datagrams (which MAY + contain arbitrary data) MUST be encoded for transport. This RFC + REQUIRES the contents of the payload to be encoded in the base-64 + encoding of RFC 2045 [RFC2045], but removes the requirement that the + encoded output MUST be wrapped on 76-character lines. + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 3] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +2.2. Example Datagram + + The following is an example IPoXML datagram with an empty payload: + + + + +
+ + + + + + + + + + + + + + + +
+ + +
+ +3. TCPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.2. + +3.1. TCP Description + + A number of items have changed from the original TCP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + To calculate the length and checksum fields of the TCP element, a + canonicalized form of the element MUST be used as in section 2.1. + + An iterative method SHOULD be used to calculate checksums as in + section 2.1. + + The payload element MUST be encoded as in section 2.1. + + + +Kennedy Informational [Page 4] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + The TCP offset element was expanded to a maximum of 255 from 16 to + allow for the increased size of the header in XML. + + TCPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +3.2. Example Datagram + + The following is an example TCPoXML datagram with an empty payload: + + + + + + + + + + + + + + + + + + + + + + + + +4. UDPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.3. + +4.1. UDP Description + + A number of items have changed from the original UDP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + + + + + + +Kennedy Informational [Page 5] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + To calculate the length and checksum fields of the UDP element, a + canonicalized form of the element MUST be used as in section 2.1. An + iterative method SHOULD be used to calculate checksums as in section + 2.1. + + The payload element MUST be encoded as in section 2.1. + + UDPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +4.2. Example Datagram + + The following is an example UDPoXML datagram with an empty payload: + + + + + + + + + + + + + + +5. Network Transport + + This document provides for the transmission of BLOAT datagrams over + two common families of physical layer transport. Future RFCs will + address additional transports as routing vendors catch up to the + specification, and we begin to see BLOAT routed across the Internet + backbone. + +5.1. Ethernet + + BLOAT is encapsulated in Ethernet datagrams as in [RFC894] with the + exception that the type field of the Ethernet frame MUST contain the + value 0xBEEF. The first 5 octets of the Ethernet frame payload will + be 0x3c 3f 78 6d 6c (" + --> + + + + +Kennedy Informational [Page 7] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 9] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 10] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + +7.2. TCPoXML DTD + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 11] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 12] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + +7.3. UDPoXML DTD + + + + + + + + + + + + + + + +Kennedy Informational [Page 13] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +8. Security Considerations + + XML, as a subset of SGML, has the same security considerations as + specified in SGML Media Types [RFC1874]. Security considerations + that apply to IP, TCP and UDP also likely apply to BLOAT as it does + not attempt to correct for issues not related to message format. + +9. References + + [JABBER] Miller, J., "Jabber", draft-miller-jabber-00.txt, + February 2002. (Work in Progress) + + [RFC768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, + August 1980. + + [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791, + September 1981. + + [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC + 793, September 1981. + + [RFC894] Hornig, C., "Standard for the Transmission of IP + Datagrams over Ethernet Networks.", RFC 894, April 1984. + + [RFC1042] Postel, J. and J. Reynolds, "Standard for the + Transmission of IP Datagrams Over IEEE 802 Networks", STD + 43, RFC 1042, February 1988. + + [RFC1123] Braden, R., "Requirements for Internet Hosts - + Application and Support", RFC 1123, October 1989. + + [RFC1874] Levinson, E., "SGML Media Types", RFC 1874, December + 1995. + + [RFC2003] Perkins, C., "IP Encapsulation within IP", RFC 2003, + October 1996. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", RFC 2279, January 1998. + + + + + +Kennedy Informational [Page 14] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6 + (IPv6) Specification", RFC 2460, December 1998. + + [RFC3080] Rose, M., "The Blocks Extensible Exchange Protocol Core", + RFC 3080, March 2001. + + [SOAP] Box, D., Ehnebuske, D., Kakivaya, G., Layman, A., + Mendelsohn, N., Nielsen, H. F., Thatte, S. Winer, D., + "Simple Object Access Protocol (SOAP) 1.1" World Wide Web + Consortium Note, May 2000 http://www.w3.org/TR/SOAP/ + + [XML] Bray, T., Paoli, J., Sperberg-McQueen, C. M., "Extensible + Markup Language (XML)" World Wide Web Consortium + Recommendation REC- xml-19980210. + http://www.w3.org/TR/1998/REC-xml-19980210 + +10. Author's Address + + Hugh Kennedy + Mimezine + 1060 West Addison + Chicago, IL 60613 + USA + + EMail: kennedyh@engin.umich.edu + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 15] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +11. Full Copyright Statement + + Copyright (C) The Internet Society (2002). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 16] + + + + + + + +Network Working Group H. Kennedy +Request for Comments: 3252 Mimezine +Category: Informational 1 April 2002 + + + Binary Lexical Octet Ad-hoc Transport + +Status of this Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2002). All Rights Reserved. + +Abstract + + This document defines a reformulation of IP and two transport layer + protocols (TCP and UDP) as XML applications. + +1. Introduction + +1.1. Overview + + This document describes the Binary Lexical Octet Ad-hoc Transport + (BLOAT): a reformulation of a widely-deployed network-layer protocol + (IP [RFC791]), and two associated transport layer protocols (TCP + [RFC793] and UDP [RFC768]) as XML [XML] applications. It also + describes methods for transporting BLOAT over Ethernet and IEEE 802 + networks as well as encapsulating BLOAT in IP for gatewaying BLOAT + across the public Internet. + +1.2. Motivation + + The wild popularity of XML as a basis for application-level protocols + such as the Blocks Extensible Exchange Protocol [RFC3080], the Simple + Object Access Protocol [SOAP], and Jabber [JABBER] prompted + investigation into the possibility of extending the use of XML in the + protocol stack. Using XML at both the transport and network layer in + addition to the application layer would provide for an amazing amount + of power and flexibility while removing dependencies on proprietary + and hard-to-understand binary protocols. This protocol unification + would also allow applications to use a single XML parser for all + aspects of their operation, eliminating developer time spent figuring + out the intricacies of each new protocol, and moving the hard work of + + + + +Kennedy Informational [Page 1] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + parsing to the XML toolset. The use of XML also mitigates concerns + over "network vs. host" byte ordering which is at the root of many + network application bugs. + +1.3. Relation to Existing Protocols + + The reformulations specified in this RFC follow as closely as + possible the spirit of the RFCs on which they are based, and so MAY + contain elements or attributes that would not be needed in a pure + reworking (e.g. length attributes, which are implicit in XML.) + + The layering of network and transport protocols are maintained in + this RFC despite the optimizations that could be made if the line + were somewhat blurred (i.e. merging TCP and IP into a single, larger + element in the DTD) in order to foster future use of this protocol as + a basis for reformulating other protocols (such as ICMP.) + + Other than the encoding, the behavioral aspects of each of the + existing protocols remain unchanged. Routing, address spaces, TCP + congestion control, etc. behave as specified in the extant standards. + Adapting to new standards and experimental algorithm heuristics for + improving performance will become much easier once the move to BLOAT + has been completed. + +1.4. Requirement Levels + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in BCP 14, RFC 2119 + [RFC2119]. + +2. IPoXML + + This protocol MUST be implemented to be compliant with this RFC. + IPoXML is the root protocol REQUIRED for effective use of TCPoXML + (section 3.) and higher-level application protocols. + + The DTD for this document type can be found in section 7.1. + + The routing of IPoXML can be easily implemented on hosts with an XML + parser, as the regular structure lends itself handily to parsing and + validation of the document/datagram and then processing the + destination address, TTL, and checksum before sending it on to its + next-hop. + + The reformulation of IPv4 was chosen over IPv6 [RFC2460] due to the + wider deployment of IPv4 and the fact that implementing IPv6 as XML + would have exceeded the 1500 byte Ethernet MTU. + + + +Kennedy Informational [Page 2] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + All BLOAT implementations MUST use - and specify - the UTF-8 encoding + of RFC 2279 [RFC2279]. All BLOAT document/datagrams MUST be well- + formed and include the XMLDecl. + +2.1. IP Description + + A number of items have changed (for the better) from the original IP + specification. Bit-masks, where present have been converted into + human-readable values. IP addresses are listed in their dotted- + decimal notation [RFC1123]. Length and checksum values are present + as decimal integers. + + To calculate the length and checksum fields of the IP element, a + canonicalized form of the element MUST be used. The canonical form + SHALL have no whitespace (including newline characters) between + elements and only one space character between attributes. There + SHALL NOT be a space following the last attribute in an element. + + An iterative method SHOULD be used to calculate checksums, as the + length field will vary based on the size of the checksum. + + The payload element bears special attention. Due to the character + set restrictions of XML, the payload of IP datagrams (which MAY + contain arbitrary data) MUST be encoded for transport. This RFC + REQUIRES the contents of the payload to be encoded in the base-64 + encoding of RFC 2045 [RFC2045], but removes the requirement that the + encoded output MUST be wrapped on 76-character lines. + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 3] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +2.2. Example Datagram + + The following is an example IPoXML datagram with an empty payload: + + + + +
+ + + + + + + + + + + + + + + +
+ + +
+ +3. TCPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.2. + +3.1. TCP Description + + A number of items have changed from the original TCP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + To calculate the length and checksum fields of the TCP element, a + canonicalized form of the element MUST be used as in section 2.1. + + An iterative method SHOULD be used to calculate checksums as in + section 2.1. + + The payload element MUST be encoded as in section 2.1. + + + +Kennedy Informational [Page 4] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + The TCP offset element was expanded to a maximum of 255 from 16 to + allow for the increased size of the header in XML. + + TCPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +3.2. Example Datagram + + The following is an example TCPoXML datagram with an empty payload: + + + + + + + + + + + + + + + + + + + + + + + + +4. UDPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.3. + +4.1. UDP Description + + A number of items have changed from the original UDP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + + + + + + +Kennedy Informational [Page 5] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + To calculate the length and checksum fields of the UDP element, a + canonicalized form of the element MUST be used as in section 2.1. An + iterative method SHOULD be used to calculate checksums as in section + 2.1. + + The payload element MUST be encoded as in section 2.1. + + UDPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +4.2. Example Datagram + + The following is an example UDPoXML datagram with an empty payload: + + + + + + + + + + + + + + +5. Network Transport + + This document provides for the transmission of BLOAT datagrams over + two common families of physical layer transport. Future RFCs will + address additional transports as routing vendors catch up to the + specification, and we begin to see BLOAT routed across the Internet + backbone. + +5.1. Ethernet + + BLOAT is encapsulated in Ethernet datagrams as in [RFC894] with the + exception that the type field of the Ethernet frame MUST contain the + value 0xBEEF. The first 5 octets of the Ethernet frame payload will + be 0x3c 3f 78 6d 6c (" + --> + + + + +Kennedy Informational [Page 7] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 9] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 10] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + +7.2. TCPoXML DTD + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 11] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 12] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + +7.3. UDPoXML DTD + + + + + + + + + + + + + + + +Kennedy Informational [Page 13] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +8. Security Considerations + + XML, as a subset of SGML, has the same security considerations as + specified in SGML Media Types [RFC1874]. Security considerations + that apply to IP, TCP and UDP also likely apply to BLOAT as it does + not attempt to correct for issues not related to message format. + +9. References + + [JABBER] Miller, J., "Jabber", draft-miller-jabber-00.txt, + February 2002. (Work in Progress) + + [RFC768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, + August 1980. + + [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791, + September 1981. + + [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC + 793, September 1981. + + [RFC894] Hornig, C., "Standard for the Transmission of IP + Datagrams over Ethernet Networks.", RFC 894, April 1984. + + [RFC1042] Postel, J. and J. Reynolds, "Standard for the + Transmission of IP Datagrams Over IEEE 802 Networks", STD + 43, RFC 1042, February 1988. + + [RFC1123] Braden, R., "Requirements for Internet Hosts - + Application and Support", RFC 1123, October 1989. + + [RFC1874] Levinson, E., "SGML Media Types", RFC 1874, December + 1995. + + [RFC2003] Perkins, C., "IP Encapsulation within IP", RFC 2003, + October 1996. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", RFC 2279, January 1998. + + + + + +Kennedy Informational [Page 14] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6 + (IPv6) Specification", RFC 2460, December 1998. + + [RFC3080] Rose, M., "The Blocks Extensible Exchange Protocol Core", + RFC 3080, March 2001. + + [SOAP] Box, D., Ehnebuske, D., Kakivaya, G., Layman, A., + Mendelsohn, N., Nielsen, H. F., Thatte, S. Winer, D., + "Simple Object Access Protocol (SOAP) 1.1" World Wide Web + Consortium Note, May 2000 http://www.w3.org/TR/SOAP/ + + [XML] Bray, T., Paoli, J., Sperberg-McQueen, C. M., "Extensible + Markup Language (XML)" World Wide Web Consortium + Recommendation REC- xml-19980210. + http://www.w3.org/TR/1998/REC-xml-19980210 + +10. Author's Address + + Hugh Kennedy + Mimezine + 1060 West Addison + Chicago, IL 60613 + USA + + EMail: kennedyh@engin.umich.edu + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 15] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +11. Full Copyright Statement + + Copyright (C) The Internet Society (2002). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 16] + + + + + + + +Network Working Group H. Kennedy +Request for Comments: 3252 Mimezine +Category: Informational 1 April 2002 + + + Binary Lexical Octet Ad-hoc Transport + +Status of this Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2002). All Rights Reserved. + +Abstract + + This document defines a reformulation of IP and two transport layer + protocols (TCP and UDP) as XML applications. + +1. Introduction + +1.1. Overview + + This document describes the Binary Lexical Octet Ad-hoc Transport + (BLOAT): a reformulation of a widely-deployed network-layer protocol + (IP [RFC791]), and two associated transport layer protocols (TCP + [RFC793] and UDP [RFC768]) as XML [XML] applications. It also + describes methods for transporting BLOAT over Ethernet and IEEE 802 + networks as well as encapsulating BLOAT in IP for gatewaying BLOAT + across the public Internet. + +1.2. Motivation + + The wild popularity of XML as a basis for application-level protocols + such as the Blocks Extensible Exchange Protocol [RFC3080], the Simple + Object Access Protocol [SOAP], and Jabber [JABBER] prompted + investigation into the possibility of extending the use of XML in the + protocol stack. Using XML at both the transport and network layer in + addition to the application layer would provide for an amazing amount + of power and flexibility while removing dependencies on proprietary + and hard-to-understand binary protocols. This protocol unification + would also allow applications to use a single XML parser for all + aspects of their operation, eliminating developer time spent figuring + out the intricacies of each new protocol, and moving the hard work of + + + + +Kennedy Informational [Page 1] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + parsing to the XML toolset. The use of XML also mitigates concerns + over "network vs. host" byte ordering which is at the root of many + network application bugs. + +1.3. Relation to Existing Protocols + + The reformulations specified in this RFC follow as closely as + possible the spirit of the RFCs on which they are based, and so MAY + contain elements or attributes that would not be needed in a pure + reworking (e.g. length attributes, which are implicit in XML.) + + The layering of network and transport protocols are maintained in + this RFC despite the optimizations that could be made if the line + were somewhat blurred (i.e. merging TCP and IP into a single, larger + element in the DTD) in order to foster future use of this protocol as + a basis for reformulating other protocols (such as ICMP.) + + Other than the encoding, the behavioral aspects of each of the + existing protocols remain unchanged. Routing, address spaces, TCP + congestion control, etc. behave as specified in the extant standards. + Adapting to new standards and experimental algorithm heuristics for + improving performance will become much easier once the move to BLOAT + has been completed. + +1.4. Requirement Levels + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in BCP 14, RFC 2119 + [RFC2119]. + +2. IPoXML + + This protocol MUST be implemented to be compliant with this RFC. + IPoXML is the root protocol REQUIRED for effective use of TCPoXML + (section 3.) and higher-level application protocols. + + The DTD for this document type can be found in section 7.1. + + The routing of IPoXML can be easily implemented on hosts with an XML + parser, as the regular structure lends itself handily to parsing and + validation of the document/datagram and then processing the + destination address, TTL, and checksum before sending it on to its + next-hop. + + The reformulation of IPv4 was chosen over IPv6 [RFC2460] due to the + wider deployment of IPv4 and the fact that implementing IPv6 as XML + would have exceeded the 1500 byte Ethernet MTU. + + + +Kennedy Informational [Page 2] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + All BLOAT implementations MUST use - and specify - the UTF-8 encoding + of RFC 2279 [RFC2279]. All BLOAT document/datagrams MUST be well- + formed and include the XMLDecl. + +2.1. IP Description + + A number of items have changed (for the better) from the original IP + specification. Bit-masks, where present have been converted into + human-readable values. IP addresses are listed in their dotted- + decimal notation [RFC1123]. Length and checksum values are present + as decimal integers. + + To calculate the length and checksum fields of the IP element, a + canonicalized form of the element MUST be used. The canonical form + SHALL have no whitespace (including newline characters) between + elements and only one space character between attributes. There + SHALL NOT be a space following the last attribute in an element. + + An iterative method SHOULD be used to calculate checksums, as the + length field will vary based on the size of the checksum. + + The payload element bears special attention. Due to the character + set restrictions of XML, the payload of IP datagrams (which MAY + contain arbitrary data) MUST be encoded for transport. This RFC + REQUIRES the contents of the payload to be encoded in the base-64 + encoding of RFC 2045 [RFC2045], but removes the requirement that the + encoded output MUST be wrapped on 76-character lines. + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 3] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +2.2. Example Datagram + + The following is an example IPoXML datagram with an empty payload: + + + + +
+ + + + + + + + + + + + + + + +
+ + +
+ +3. TCPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.2. + +3.1. TCP Description + + A number of items have changed from the original TCP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + To calculate the length and checksum fields of the TCP element, a + canonicalized form of the element MUST be used as in section 2.1. + + An iterative method SHOULD be used to calculate checksums as in + section 2.1. + + The payload element MUST be encoded as in section 2.1. + + + +Kennedy Informational [Page 4] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + The TCP offset element was expanded to a maximum of 255 from 16 to + allow for the increased size of the header in XML. + + TCPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +3.2. Example Datagram + + The following is an example TCPoXML datagram with an empty payload: + + + + + + + + + + + + + + + + + + + + + + + + +4. UDPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.3. + +4.1. UDP Description + + A number of items have changed from the original UDP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + + + + + + +Kennedy Informational [Page 5] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + To calculate the length and checksum fields of the UDP element, a + canonicalized form of the element MUST be used as in section 2.1. An + iterative method SHOULD be used to calculate checksums as in section + 2.1. + + The payload element MUST be encoded as in section 2.1. + + UDPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +4.2. Example Datagram + + The following is an example UDPoXML datagram with an empty payload: + + + + + + + + + + + + + + +5. Network Transport + + This document provides for the transmission of BLOAT datagrams over + two common families of physical layer transport. Future RFCs will + address additional transports as routing vendors catch up to the + specification, and we begin to see BLOAT routed across the Internet + backbone. + +5.1. Ethernet + + BLOAT is encapsulated in Ethernet datagrams as in [RFC894] with the + exception that the type field of the Ethernet frame MUST contain the + value 0xBEEF. The first 5 octets of the Ethernet frame payload will + be 0x3c 3f 78 6d 6c (" + --> + + + + +Kennedy Informational [Page 7] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 9] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 10] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + +7.2. TCPoXML DTD + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 11] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 12] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + +7.3. UDPoXML DTD + + + + + + + + + + + + + + + +Kennedy Informational [Page 13] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +8. Security Considerations + + XML, as a subset of SGML, has the same security considerations as + specified in SGML Media Types [RFC1874]. Security considerations + that apply to IP, TCP and UDP also likely apply to BLOAT as it does + not attempt to correct for issues not related to message format. + +9. References + + [JABBER] Miller, J., "Jabber", draft-miller-jabber-00.txt, + February 2002. (Work in Progress) + + [RFC768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, + August 1980. + + [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791, + September 1981. + + [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC + 793, September 1981. + + [RFC894] Hornig, C., "Standard for the Transmission of IP + Datagrams over Ethernet Networks.", RFC 894, April 1984. + + [RFC1042] Postel, J. and J. Reynolds, "Standard for the + Transmission of IP Datagrams Over IEEE 802 Networks", STD + 43, RFC 1042, February 1988. + + [RFC1123] Braden, R., "Requirements for Internet Hosts - + Application and Support", RFC 1123, October 1989. + + [RFC1874] Levinson, E., "SGML Media Types", RFC 1874, December + 1995. + + [RFC2003] Perkins, C., "IP Encapsulation within IP", RFC 2003, + October 1996. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", RFC 2279, January 1998. + + + + + +Kennedy Informational [Page 14] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6 + (IPv6) Specification", RFC 2460, December 1998. + + [RFC3080] Rose, M., "The Blocks Extensible Exchange Protocol Core", + RFC 3080, March 2001. + + [SOAP] Box, D., Ehnebuske, D., Kakivaya, G., Layman, A., + Mendelsohn, N., Nielsen, H. F., Thatte, S. Winer, D., + "Simple Object Access Protocol (SOAP) 1.1" World Wide Web + Consortium Note, May 2000 http://www.w3.org/TR/SOAP/ + + [XML] Bray, T., Paoli, J., Sperberg-McQueen, C. M., "Extensible + Markup Language (XML)" World Wide Web Consortium + Recommendation REC- xml-19980210. + http://www.w3.org/TR/1998/REC-xml-19980210 + +10. Author's Address + + Hugh Kennedy + Mimezine + 1060 West Addison + Chicago, IL 60613 + USA + + EMail: kennedyh@engin.umich.edu + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 15] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +11. Full Copyright Statement + + Copyright (C) The Internet Society (2002). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 16] + + + + + + + +Network Working Group H. Kennedy +Request for Comments: 3252 Mimezine +Category: Informational 1 April 2002 + + + Binary Lexical Octet Ad-hoc Transport + +Status of this Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2002). All Rights Reserved. + +Abstract + + This document defines a reformulation of IP and two transport layer + protocols (TCP and UDP) as XML applications. + +1. Introduction + +1.1. Overview + + This document describes the Binary Lexical Octet Ad-hoc Transport + (BLOAT): a reformulation of a widely-deployed network-layer protocol + (IP [RFC791]), and two associated transport layer protocols (TCP + [RFC793] and UDP [RFC768]) as XML [XML] applications. It also + describes methods for transporting BLOAT over Ethernet and IEEE 802 + networks as well as encapsulating BLOAT in IP for gatewaying BLOAT + across the public Internet. + +1.2. Motivation + + The wild popularity of XML as a basis for application-level protocols + such as the Blocks Extensible Exchange Protocol [RFC3080], the Simple + Object Access Protocol [SOAP], and Jabber [JABBER] prompted + investigation into the possibility of extending the use of XML in the + protocol stack. Using XML at both the transport and network layer in + addition to the application layer would provide for an amazing amount + of power and flexibility while removing dependencies on proprietary + and hard-to-understand binary protocols. This protocol unification + would also allow applications to use a single XML parser for all + aspects of their operation, eliminating developer time spent figuring + out the intricacies of each new protocol, and moving the hard work of + + + + +Kennedy Informational [Page 1] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + parsing to the XML toolset. The use of XML also mitigates concerns + over "network vs. host" byte ordering which is at the root of many + network application bugs. + +1.3. Relation to Existing Protocols + + The reformulations specified in this RFC follow as closely as + possible the spirit of the RFCs on which they are based, and so MAY + contain elements or attributes that would not be needed in a pure + reworking (e.g. length attributes, which are implicit in XML.) + + The layering of network and transport protocols are maintained in + this RFC despite the optimizations that could be made if the line + were somewhat blurred (i.e. merging TCP and IP into a single, larger + element in the DTD) in order to foster future use of this protocol as + a basis for reformulating other protocols (such as ICMP.) + + Other than the encoding, the behavioral aspects of each of the + existing protocols remain unchanged. Routing, address spaces, TCP + congestion control, etc. behave as specified in the extant standards. + Adapting to new standards and experimental algorithm heuristics for + improving performance will become much easier once the move to BLOAT + has been completed. + +1.4. Requirement Levels + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in BCP 14, RFC 2119 + [RFC2119]. + +2. IPoXML + + This protocol MUST be implemented to be compliant with this RFC. + IPoXML is the root protocol REQUIRED for effective use of TCPoXML + (section 3.) and higher-level application protocols. + + The DTD for this document type can be found in section 7.1. + + The routing of IPoXML can be easily implemented on hosts with an XML + parser, as the regular structure lends itself handily to parsing and + validation of the document/datagram and then processing the + destination address, TTL, and checksum before sending it on to its + next-hop. + + The reformulation of IPv4 was chosen over IPv6 [RFC2460] due to the + wider deployment of IPv4 and the fact that implementing IPv6 as XML + would have exceeded the 1500 byte Ethernet MTU. + + + +Kennedy Informational [Page 2] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + All BLOAT implementations MUST use - and specify - the UTF-8 encoding + of RFC 2279 [RFC2279]. All BLOAT document/datagrams MUST be well- + formed and include the XMLDecl. + +2.1. IP Description + + A number of items have changed (for the better) from the original IP + specification. Bit-masks, where present have been converted into + human-readable values. IP addresses are listed in their dotted- + decimal notation [RFC1123]. Length and checksum values are present + as decimal integers. + + To calculate the length and checksum fields of the IP element, a + canonicalized form of the element MUST be used. The canonical form + SHALL have no whitespace (including newline characters) between + elements and only one space character between attributes. There + SHALL NOT be a space following the last attribute in an element. + + An iterative method SHOULD be used to calculate checksums, as the + length field will vary based on the size of the checksum. + + The payload element bears special attention. Due to the character + set restrictions of XML, the payload of IP datagrams (which MAY + contain arbitrary data) MUST be encoded for transport. This RFC + REQUIRES the contents of the payload to be encoded in the base-64 + encoding of RFC 2045 [RFC2045], but removes the requirement that the + encoded output MUST be wrapped on 76-character lines. + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 3] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +2.2. Example Datagram + + The following is an example IPoXML datagram with an empty payload: + + + + +
+ + + + + + + + + + + + + + + +
+ + +
+ +3. TCPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.2. + +3.1. TCP Description + + A number of items have changed from the original TCP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + To calculate the length and checksum fields of the TCP element, a + canonicalized form of the element MUST be used as in section 2.1. + + An iterative method SHOULD be used to calculate checksums as in + section 2.1. + + The payload element MUST be encoded as in section 2.1. + + + +Kennedy Informational [Page 4] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + The TCP offset element was expanded to a maximum of 255 from 16 to + allow for the increased size of the header in XML. + + TCPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +3.2. Example Datagram + + The following is an example TCPoXML datagram with an empty payload: + + + + + + + + + + + + + + + + + + + + + + + + +4. UDPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.3. + +4.1. UDP Description + + A number of items have changed from the original UDP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + + + + + + +Kennedy Informational [Page 5] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + To calculate the length and checksum fields of the UDP element, a + canonicalized form of the element MUST be used as in section 2.1. An + iterative method SHOULD be used to calculate checksums as in section + 2.1. + + The payload element MUST be encoded as in section 2.1. + + UDPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +4.2. Example Datagram + + The following is an example UDPoXML datagram with an empty payload: + + + + + + + + + + + + + + +5. Network Transport + + This document provides for the transmission of BLOAT datagrams over + two common families of physical layer transport. Future RFCs will + address additional transports as routing vendors catch up to the + specification, and we begin to see BLOAT routed across the Internet + backbone. + +5.1. Ethernet + + BLOAT is encapsulated in Ethernet datagrams as in [RFC894] with the + exception that the type field of the Ethernet frame MUST contain the + value 0xBEEF. The first 5 octets of the Ethernet frame payload will + be 0x3c 3f 78 6d 6c (" + --> + + + + +Kennedy Informational [Page 7] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 9] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 10] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + +7.2. TCPoXML DTD + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 11] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 12] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + +7.3. UDPoXML DTD + + + + + + + + + + + + + + + +Kennedy Informational [Page 13] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +8. Security Considerations + + XML, as a subset of SGML, has the same security considerations as + specified in SGML Media Types [RFC1874]. Security considerations + that apply to IP, TCP and UDP also likely apply to BLOAT as it does + not attempt to correct for issues not related to message format. + +9. References + + [JABBER] Miller, J., "Jabber", draft-miller-jabber-00.txt, + February 2002. (Work in Progress) + + [RFC768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, + August 1980. + + [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791, + September 1981. + + [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC + 793, September 1981. + + [RFC894] Hornig, C., "Standard for the Transmission of IP + Datagrams over Ethernet Networks.", RFC 894, April 1984. + + [RFC1042] Postel, J. and J. Reynolds, "Standard for the + Transmission of IP Datagrams Over IEEE 802 Networks", STD + 43, RFC 1042, February 1988. + + [RFC1123] Braden, R., "Requirements for Internet Hosts - + Application and Support", RFC 1123, October 1989. + + [RFC1874] Levinson, E., "SGML Media Types", RFC 1874, December + 1995. + + [RFC2003] Perkins, C., "IP Encapsulation within IP", RFC 2003, + October 1996. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", RFC 2279, January 1998. + + + + + +Kennedy Informational [Page 14] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6 + (IPv6) Specification", RFC 2460, December 1998. + + [RFC3080] Rose, M., "The Blocks Extensible Exchange Protocol Core", + RFC 3080, March 2001. + + [SOAP] Box, D., Ehnebuske, D., Kakivaya, G., Layman, A., + Mendelsohn, N., Nielsen, H. F., Thatte, S. Winer, D., + "Simple Object Access Protocol (SOAP) 1.1" World Wide Web + Consortium Note, May 2000 http://www.w3.org/TR/SOAP/ + + [XML] Bray, T., Paoli, J., Sperberg-McQueen, C. M., "Extensible + Markup Language (XML)" World Wide Web Consortium + Recommendation REC- xml-19980210. + http://www.w3.org/TR/1998/REC-xml-19980210 + +10. Author's Address + + Hugh Kennedy + Mimezine + 1060 West Addison + Chicago, IL 60613 + USA + + EMail: kennedyh@engin.umich.edu + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 15] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +11. Full Copyright Statement + + Copyright (C) The Internet Society (2002). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 16] + + + + + + + +Network Working Group H. Kennedy +Request for Comments: 3252 Mimezine +Category: Informational 1 April 2002 + + + Binary Lexical Octet Ad-hoc Transport + +Status of this Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2002). All Rights Reserved. + +Abstract + + This document defines a reformulation of IP and two transport layer + protocols (TCP and UDP) as XML applications. + +1. Introduction + +1.1. Overview + + This document describes the Binary Lexical Octet Ad-hoc Transport + (BLOAT): a reformulation of a widely-deployed network-layer protocol + (IP [RFC791]), and two associated transport layer protocols (TCP + [RFC793] and UDP [RFC768]) as XML [XML] applications. It also + describes methods for transporting BLOAT over Ethernet and IEEE 802 + networks as well as encapsulating BLOAT in IP for gatewaying BLOAT + across the public Internet. + +1.2. Motivation + + The wild popularity of XML as a basis for application-level protocols + such as the Blocks Extensible Exchange Protocol [RFC3080], the Simple + Object Access Protocol [SOAP], and Jabber [JABBER] prompted + investigation into the possibility of extending the use of XML in the + protocol stack. Using XML at both the transport and network layer in + addition to the application layer would provide for an amazing amount + of power and flexibility while removing dependencies on proprietary + and hard-to-understand binary protocols. This protocol unification + would also allow applications to use a single XML parser for all + aspects of their operation, eliminating developer time spent figuring + out the intricacies of each new protocol, and moving the hard work of + + + + +Kennedy Informational [Page 1] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + parsing to the XML toolset. The use of XML also mitigates concerns + over "network vs. host" byte ordering which is at the root of many + network application bugs. + +1.3. Relation to Existing Protocols + + The reformulations specified in this RFC follow as closely as + possible the spirit of the RFCs on which they are based, and so MAY + contain elements or attributes that would not be needed in a pure + reworking (e.g. length attributes, which are implicit in XML.) + + The layering of network and transport protocols are maintained in + this RFC despite the optimizations that could be made if the line + were somewhat blurred (i.e. merging TCP and IP into a single, larger + element in the DTD) in order to foster future use of this protocol as + a basis for reformulating other protocols (such as ICMP.) + + Other than the encoding, the behavioral aspects of each of the + existing protocols remain unchanged. Routing, address spaces, TCP + congestion control, etc. behave as specified in the extant standards. + Adapting to new standards and experimental algorithm heuristics for + improving performance will become much easier once the move to BLOAT + has been completed. + +1.4. Requirement Levels + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in BCP 14, RFC 2119 + [RFC2119]. + +2. IPoXML + + This protocol MUST be implemented to be compliant with this RFC. + IPoXML is the root protocol REQUIRED for effective use of TCPoXML + (section 3.) and higher-level application protocols. + + The DTD for this document type can be found in section 7.1. + + The routing of IPoXML can be easily implemented on hosts with an XML + parser, as the regular structure lends itself handily to parsing and + validation of the document/datagram and then processing the + destination address, TTL, and checksum before sending it on to its + next-hop. + + The reformulation of IPv4 was chosen over IPv6 [RFC2460] due to the + wider deployment of IPv4 and the fact that implementing IPv6 as XML + would have exceeded the 1500 byte Ethernet MTU. + + + +Kennedy Informational [Page 2] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + All BLOAT implementations MUST use - and specify - the UTF-8 encoding + of RFC 2279 [RFC2279]. All BLOAT document/datagrams MUST be well- + formed and include the XMLDecl. + +2.1. IP Description + + A number of items have changed (for the better) from the original IP + specification. Bit-masks, where present have been converted into + human-readable values. IP addresses are listed in their dotted- + decimal notation [RFC1123]. Length and checksum values are present + as decimal integers. + + To calculate the length and checksum fields of the IP element, a + canonicalized form of the element MUST be used. The canonical form + SHALL have no whitespace (including newline characters) between + elements and only one space character between attributes. There + SHALL NOT be a space following the last attribute in an element. + + An iterative method SHOULD be used to calculate checksums, as the + length field will vary based on the size of the checksum. + + The payload element bears special attention. Due to the character + set restrictions of XML, the payload of IP datagrams (which MAY + contain arbitrary data) MUST be encoded for transport. This RFC + REQUIRES the contents of the payload to be encoded in the base-64 + encoding of RFC 2045 [RFC2045], but removes the requirement that the + encoded output MUST be wrapped on 76-character lines. + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 3] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +2.2. Example Datagram + + The following is an example IPoXML datagram with an empty payload: + + + + +
+ + + + + + + + + + + + + + + +
+ + +
+ +3. TCPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.2. + +3.1. TCP Description + + A number of items have changed from the original TCP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + To calculate the length and checksum fields of the TCP element, a + canonicalized form of the element MUST be used as in section 2.1. + + An iterative method SHOULD be used to calculate checksums as in + section 2.1. + + The payload element MUST be encoded as in section 2.1. + + + +Kennedy Informational [Page 4] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + The TCP offset element was expanded to a maximum of 255 from 16 to + allow for the increased size of the header in XML. + + TCPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +3.2. Example Datagram + + The following is an example TCPoXML datagram with an empty payload: + + + + + + + + + + + + + + + + + + + + + + + + +4. UDPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.3. + +4.1. UDP Description + + A number of items have changed from the original UDP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + + + + + + +Kennedy Informational [Page 5] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + To calculate the length and checksum fields of the UDP element, a + canonicalized form of the element MUST be used as in section 2.1. An + iterative method SHOULD be used to calculate checksums as in section + 2.1. + + The payload element MUST be encoded as in section 2.1. + + UDPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +4.2. Example Datagram + + The following is an example UDPoXML datagram with an empty payload: + + + + + + + + + + + + + + +5. Network Transport + + This document provides for the transmission of BLOAT datagrams over + two common families of physical layer transport. Future RFCs will + address additional transports as routing vendors catch up to the + specification, and we begin to see BLOAT routed across the Internet + backbone. + +5.1. Ethernet + + BLOAT is encapsulated in Ethernet datagrams as in [RFC894] with the + exception that the type field of the Ethernet frame MUST contain the + value 0xBEEF. The first 5 octets of the Ethernet frame payload will + be 0x3c 3f 78 6d 6c (" + --> + + + + +Kennedy Informational [Page 7] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 9] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 10] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + +7.2. TCPoXML DTD + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 11] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 12] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + +7.3. UDPoXML DTD + + + + + + + + + + + + + + + +Kennedy Informational [Page 13] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +8. Security Considerations + + XML, as a subset of SGML, has the same security considerations as + specified in SGML Media Types [RFC1874]. Security considerations + that apply to IP, TCP and UDP also likely apply to BLOAT as it does + not attempt to correct for issues not related to message format. + +9. References + + [JABBER] Miller, J., "Jabber", draft-miller-jabber-00.txt, + February 2002. (Work in Progress) + + [RFC768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, + August 1980. + + [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791, + September 1981. + + [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC + 793, September 1981. + + [RFC894] Hornig, C., "Standard for the Transmission of IP + Datagrams over Ethernet Networks.", RFC 894, April 1984. + + [RFC1042] Postel, J. and J. Reynolds, "Standard for the + Transmission of IP Datagrams Over IEEE 802 Networks", STD + 43, RFC 1042, February 1988. + + [RFC1123] Braden, R., "Requirements for Internet Hosts - + Application and Support", RFC 1123, October 1989. + + [RFC1874] Levinson, E., "SGML Media Types", RFC 1874, December + 1995. + + [RFC2003] Perkins, C., "IP Encapsulation within IP", RFC 2003, + October 1996. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", RFC 2279, January 1998. + + + + + +Kennedy Informational [Page 14] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6 + (IPv6) Specification", RFC 2460, December 1998. + + [RFC3080] Rose, M., "The Blocks Extensible Exchange Protocol Core", + RFC 3080, March 2001. + + [SOAP] Box, D., Ehnebuske, D., Kakivaya, G., Layman, A., + Mendelsohn, N., Nielsen, H. F., Thatte, S. Winer, D., + "Simple Object Access Protocol (SOAP) 1.1" World Wide Web + Consortium Note, May 2000 http://www.w3.org/TR/SOAP/ + + [XML] Bray, T., Paoli, J., Sperberg-McQueen, C. M., "Extensible + Markup Language (XML)" World Wide Web Consortium + Recommendation REC- xml-19980210. + http://www.w3.org/TR/1998/REC-xml-19980210 + +10. Author's Address + + Hugh Kennedy + Mimezine + 1060 West Addison + Chicago, IL 60613 + USA + + EMail: kennedyh@engin.umich.edu + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 15] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +11. Full Copyright Statement + + Copyright (C) The Internet Society (2002). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 16] + diff --git a/tests/auto/network/access/qnetworkreply/certs/aspiriniks.ca.crt b/tests/auto/network/access/qnetworkreply/certs/aspiriniks.ca.crt new file mode 100644 index 0000000000..36436b6248 --- /dev/null +++ b/tests/auto/network/access/qnetworkreply/certs/aspiriniks.ca.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDnDCCAoQCCQDV3otC4hs2KTANBgkqhkiG9w0BAQUFADCBjzELMAkGA1UEBhMC +Tk8xDTALBgNVBAgTBE9zbG8xDTALBgNVBAcTBE9zbG8xDzANBgNVBAoTBlRUIEFT +QTEOMAwGA1UECxMFUVQgU1cxHDAaBgNVBAMTE2FzcGlyaW5pa3MudHJvbGwubm8x +IzAhBgkqhkiG9w0BCQEWFGFiYWJpY0B0cm9sbHRlY2guY29tMB4XDTA4MTEwMTA4 +NTcyOFoXDTA5MTEwMTA4NTcyOFowgY8xCzAJBgNVBAYTAk5PMQ0wCwYDVQQIEwRP +c2xvMQ0wCwYDVQQHEwRPc2xvMQ8wDQYDVQQKEwZUVCBBU0ExDjAMBgNVBAsTBVFU +IFNXMRwwGgYDVQQDExNhc3BpcmluaWtzLnRyb2xsLm5vMSMwIQYJKoZIhvcNAQkB +FhRhYmFiaWNAdHJvbGx0ZWNoLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAMV2bMD1DN3DMgbxU3DXw2i7EWGDXcWjTDtdHvqgIb+9nHqo3MJSrzJy +qgEPoOsXqswMla9wDPZAsWv5gVAmVSqpy2lfEgfY7LaSHiGD75seF7zIy+CxREHW +DofHXpJGGJpBCZEKQt2HfHu3+yAYNPucN78tWNZAcPbUg5tfxMZeepRimAZNIxBI +93SDrl/f9Ka7hvPSzUQsnp8hfdpHlFPFznKfD6yPrjxgz2mT9efavJ4DhtyIa4m+ +paiX515CidDz4A8CFxKZbYvuqq1ilibF/si2so9VhALC77ZcAJP1IMuT8T+WUCxq +skJqiSCncl0Hgr+ba8MDGF9UQYowgjMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA +KcJuNUHvjB8ok3cnTmQEeF0LPPkgj28Tqb5TFB8xpVfRI+wvTYsHsmGdOKCgYJ3a +7VflIsr63ojG8/rXK8H/cx2o2f2Hr3liJdi1UnoLDDRjBqGGz7JNuMreYokPvIbm +eP01mVyK4PO2iYRwHUIAw5eeB1vMWKX2z95MupD+HRLtmGyaLALg8aQxj5N84Ewl +eU2PQfhv8A1wj7aL17kfEUxDerQ1kUzlThJMV1J8Dl0l4C9N8evQkelROJU00i46 +oJikA8BW6EpgbnGyNyyj5Loy4wLPKew9nTS8MCJ5xPMQc0urbY/VzuOeUK7WQof7 +xOFSsRAVyQv+yqgmcZMCtg== +-----END CERTIFICATE----- diff --git a/tests/auto/network/access/qnetworkreply/certs/fluke.cert b/tests/auto/network/access/qnetworkreply/certs/fluke.cert new file mode 100644 index 0000000000..069fa6b341 --- /dev/null +++ b/tests/auto/network/access/qnetworkreply/certs/fluke.cert @@ -0,0 +1,75 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 0 (0x0) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=NO, ST=Oslo, L=Nydalen, O=Nokia Corporation and/or its subsidiary(-ies), OU=Development, CN=fluke.troll.no/emailAddress=ahanssen@trolltech.com + Validity + Not Before: Dec 4 01:10:32 2007 GMT + Not After : Apr 21 01:10:32 2035 GMT + Subject: C=NO, ST=Oslo, O=Nokia Corporation and/or its subsidiary(-ies), OU=Development, CN=fluke.troll.no + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (1024 bit) + Modulus (1024 bit): + 00:a7:c8:a0:4a:c4:19:05:1b:66:ba:32:e2:d2:f1: + 1c:6f:17:82:e4:39:2e:01:51:90:db:04:34:32:11: + 21:c2:0d:6f:59:d8:53:90:54:3f:83:8f:a9:d3:b3: + d5:ee:1a:9b:80:ae:c3:25:c9:5e:a5:af:4b:60:05: + aa:a0:d1:91:01:1f:ca:04:83:e3:58:1c:99:32:45: + 84:70:72:58:03:98:4a:63:8b:41:f5:08:49:d2:91: + 02:60:6b:e4:64:fe:dd:a0:aa:74:08:e9:34:4c:91: + 5f:12:3d:37:4d:54:2c:ad:7f:5b:98:60:36:02:8c: + 3b:f6:45:f3:27:6a:9b:94:9d + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Netscape Comment: + OpenSSL Generated Certificate + X509v3 Subject Key Identifier: + 21:85:04:3D:23:01:66:E5:F7:9F:1A:84:24:8A:AF:0A:79:F4:E5:AC + X509v3 Authority Key Identifier: + DirName:/C=NO/ST=Oslo/L=Nydalen/O=Nokia Corporation and/or its subsidiary(-ies)/OU=Development/CN=fluke.troll.no/emailAddress=ahanssen@trolltech.com + serial:8E:A8:B4:E8:91:B7:54:2E + + Signature Algorithm: sha1WithRSAEncryption + 6d:57:5f:d1:05:43:f0:62:05:ec:2a:71:a5:dc:19:08:f2:c4: + a6:bd:bb:25:d9:ca:89:01:0e:e4:cf:1f:c1:8c:c8:24:18:35: + 53:59:7b:c0:43:b4:32:e6:98:b2:a6:ef:15:05:0b:48:5f:e1: + a0:0c:97:a9:a1:77:d8:35:18:30:bc:a9:8f:d3:b7:54:c7:f1: + a9:9e:5d:e6:19:bf:f6:3c:5b:2b:d8:e4:3e:62:18:88:8b:d3: + 24:e1:40:9b:0c:e6:29:16:62:ab:ea:05:24:70:36:aa:55:93: + ef:02:81:1b:23:10:a2:04:eb:56:95:75:fc:f8:94:b1:5d:42: + c5:3f:36:44:85:5d:3a:2e:90:46:8a:a2:b9:6f:87:ae:0c:15: + 40:19:31:90:fc:3b:25:bb:ae:f1:66:13:0d:85:90:d9:49:34: + 8f:f2:5d:f9:7a:db:4d:5d:27:f6:76:9d:35:8c:06:a6:4c:a3: + b1:b2:b6:6f:1d:d7:a3:00:fd:72:eb:9e:ea:44:a1:af:21:34: + 7d:c7:42:e2:49:91:19:8b:c0:ad:ba:82:80:a8:71:70:f4:35: + 31:91:63:84:20:95:e9:60:af:64:8b:cc:ff:3d:8a:76:74:3d: + c8:55:6d:e4:8e:c3:2b:1c:e8:42:18:ae:9f:e6:6b:9c:34:06: + ec:6a:f2:c3 +-----BEGIN CERTIFICATE----- +MIIEEzCCAvugAwIBAgIBADANBgkqhkiG9w0BAQUFADCBnDELMAkGA1UEBhMCTk8x +DTALBgNVBAgTBE9zbG8xEDAOBgNVBAcTB055ZGFsZW4xFjAUBgNVBAoTDVRyb2xs +dGVjaCBBU0ExFDASBgNVBAsTC0RldmVsb3BtZW50MRcwFQYDVQQDEw5mbHVrZS50 +cm9sbC5ubzElMCMGCSqGSIb3DQEJARYWYWhhbnNzZW5AdHJvbGx0ZWNoLmNvbTAe +Fw0wNzEyMDQwMTEwMzJaFw0zNTA0MjEwMTEwMzJaMGMxCzAJBgNVBAYTAk5PMQ0w +CwYDVQQIEwRPc2xvMRYwFAYDVQQKEw1Ucm9sbHRlY2ggQVNBMRQwEgYDVQQLEwtE +ZXZlbG9wbWVudDEXMBUGA1UEAxMOZmx1a2UudHJvbGwubm8wgZ8wDQYJKoZIhvcN +AQEBBQADgY0AMIGJAoGBAKfIoErEGQUbZroy4tLxHG8XguQ5LgFRkNsENDIRIcIN +b1nYU5BUP4OPqdOz1e4am4CuwyXJXqWvS2AFqqDRkQEfygSD41gcmTJFhHByWAOY +SmOLQfUISdKRAmBr5GT+3aCqdAjpNEyRXxI9N01ULK1/W5hgNgKMO/ZF8ydqm5Sd +AgMBAAGjggEaMIIBFjAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NM +IEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUIYUEPSMBZuX3nxqEJIqv +Cnn05awwgbsGA1UdIwSBszCBsKGBoqSBnzCBnDELMAkGA1UEBhMCTk8xDTALBgNV +BAgTBE9zbG8xEDAOBgNVBAcTB055ZGFsZW4xFjAUBgNVBAoTDVRyb2xsdGVjaCBB +U0ExFDASBgNVBAsTC0RldmVsb3BtZW50MRcwFQYDVQQDEw5mbHVrZS50cm9sbC5u +bzElMCMGCSqGSIb3DQEJARYWYWhhbnNzZW5AdHJvbGx0ZWNoLmNvbYIJAI6otOiR +t1QuMA0GCSqGSIb3DQEBBQUAA4IBAQBtV1/RBUPwYgXsKnGl3BkI8sSmvbsl2cqJ +AQ7kzx/BjMgkGDVTWXvAQ7Qy5piypu8VBQtIX+GgDJepoXfYNRgwvKmP07dUx/Gp +nl3mGb/2PFsr2OQ+YhiIi9Mk4UCbDOYpFmKr6gUkcDaqVZPvAoEbIxCiBOtWlXX8 ++JSxXULFPzZEhV06LpBGiqK5b4euDBVAGTGQ/Dslu67xZhMNhZDZSTSP8l35ettN +XSf2dp01jAamTKOxsrZvHdejAP1y657qRKGvITR9x0LiSZEZi8CtuoKAqHFw9DUx +kWOEIJXpYK9ki8z/PYp2dD3IVW3kjsMrHOhCGK6f5mucNAbsavLD +-----END CERTIFICATE----- diff --git a/tests/auto/network/access/qnetworkreply/certs/fluke.key b/tests/auto/network/access/qnetworkreply/certs/fluke.key new file mode 100644 index 0000000000..9d1664d609 --- /dev/null +++ b/tests/auto/network/access/qnetworkreply/certs/fluke.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQCnyKBKxBkFG2a6MuLS8RxvF4LkOS4BUZDbBDQyESHCDW9Z2FOQ +VD+Dj6nTs9XuGpuArsMlyV6lr0tgBaqg0ZEBH8oEg+NYHJkyRYRwclgDmEpji0H1 +CEnSkQJga+Rk/t2gqnQI6TRMkV8SPTdNVCytf1uYYDYCjDv2RfMnapuUnQIDAQAB +AoGANFzLkanTeSGNFM0uttBipFT9F4a00dqHz6JnO7zXAT26I5r8sU1pqQBb6uLz +/+Qz5Zwk8RUAQcsMRgJetuPQUb0JZjF6Duv24hNazqXBCu7AZzUenjafwmKC/8ri +KpX3fTwqzfzi//FKGgbXQ80yykSSliDL3kn/drATxsLCgQECQQDXhEFWLJ0vVZ1s +1Ekf+3NITE+DR16X+LQ4W6vyEHAjTbaNWtcTKdAWLA2l6N4WAAPYSi6awm+zMxx4 +VomVTsjdAkEAx0z+e7natLeFcrrq8pbU+wa6SAP1VfhQWKitxL1e7u/QO90NCpxE +oQYKzMkmmpOOFjQwEMAy1dvFMbm4LHlewQJAC/ksDBaUcQHHqjktCtrUb8rVjAyW +A8lscckeB2fEYyG5J6dJVaY4ClNOOs5yMDS2Afk1F6H/xKvtQ/5CzInA/QJATDub +K+BPU8jO9q+gpuIi3VIZdupssVGmCgObVCHLakG4uO04y9IyPhV9lA9tALtoIf4c +VIvv5fWGXBrZ48kZAQJBAJmVCdzQxd9LZI5vxijUCj5EI4e+x5DRqVUvyP8KCZrC +AiNyoDP85T+hBZaSXK3aYGpVwelyj3bvo1GrTNwNWLw= +-----END RSA PRIVATE KEY----- diff --git a/tests/auto/network/access/qnetworkreply/certs/qt-test-server-cacert.pem b/tests/auto/network/access/qnetworkreply/certs/qt-test-server-cacert.pem new file mode 100644 index 0000000000..25bd4046e8 --- /dev/null +++ b/tests/auto/network/access/qnetworkreply/certs/qt-test-server-cacert.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICrTCCAhYCCQCdDn5rci6VDjANBgkqhkiG9w0BAQQFADCBmjEOMAwGA1UEChMF +Tm9raWExFDASBgNVBAsTC1F0IFNvZnR3YXJlMSIwIAYJKoZIhvcNAQkBFhNub2Jv +ZHlAbm9kb21haW4ub3JnMQ0wCwYDVQQHEwRPc2xvMQ0wCwYDVQQIEwRPc2xvMQsw +CQYDVQQGEwJOTzEjMCEGA1UEAxMacXQtdGVzdC1zZXJ2ZXIucXQtdGVzdC1uZXQw +HhcNMDkwNzEwMDc0MTIzWhcNMTkwNzA4MDc0MTIzWjCBmjEOMAwGA1UEChMFTm9r +aWExFDASBgNVBAsTC1F0IFNvZnR3YXJlMSIwIAYJKoZIhvcNAQkBFhNub2JvZHlA +bm9kb21haW4ub3JnMQ0wCwYDVQQHEwRPc2xvMQ0wCwYDVQQIEwRPc2xvMQswCQYD +VQQGEwJOTzEjMCEGA1UEAxMacXQtdGVzdC1zZXJ2ZXIucXQtdGVzdC1uZXQwgZ8w +DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM2q22/WNMmn8cC+5EEYGeICySLmp9W6 +Ay6eKHr0Xxp3X3epETuPfvAuxp7rOtkS18EMUegkUj8jw0IMEcbyHKFC/rTCaYOt +93CxGBXMIChiMPAsFeYzGa/D6xzAkfcRaJRQ+Ek3CDLXPnXfo7xpABXezYcPXAJr +gsgBfWrwHdxzAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEAy7YOLCZABQy2Ygkchq1I ++TUpvMn+gLwAyW8TNErM1V4lNY2+K78RawzKx3SqM97ymCy4TD45EA3A2gmi32NI +xSKBNjFyzngUqsXBdcSasALiowlZCiJrGwlGX5qCkBlxXvJeUEbuJLPYVl5FBjXZ +6o00K4cSPCqtqUez7WSmDZU= +-----END CERTIFICATE----- diff --git a/tests/auto/network/access/qnetworkreply/certs/server.key b/tests/auto/network/access/qnetworkreply/certs/server.key new file mode 100644 index 0000000000..9d1664d609 --- /dev/null +++ b/tests/auto/network/access/qnetworkreply/certs/server.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQCnyKBKxBkFG2a6MuLS8RxvF4LkOS4BUZDbBDQyESHCDW9Z2FOQ +VD+Dj6nTs9XuGpuArsMlyV6lr0tgBaqg0ZEBH8oEg+NYHJkyRYRwclgDmEpji0H1 +CEnSkQJga+Rk/t2gqnQI6TRMkV8SPTdNVCytf1uYYDYCjDv2RfMnapuUnQIDAQAB +AoGANFzLkanTeSGNFM0uttBipFT9F4a00dqHz6JnO7zXAT26I5r8sU1pqQBb6uLz +/+Qz5Zwk8RUAQcsMRgJetuPQUb0JZjF6Duv24hNazqXBCu7AZzUenjafwmKC/8ri +KpX3fTwqzfzi//FKGgbXQ80yykSSliDL3kn/drATxsLCgQECQQDXhEFWLJ0vVZ1s +1Ekf+3NITE+DR16X+LQ4W6vyEHAjTbaNWtcTKdAWLA2l6N4WAAPYSi6awm+zMxx4 +VomVTsjdAkEAx0z+e7natLeFcrrq8pbU+wa6SAP1VfhQWKitxL1e7u/QO90NCpxE +oQYKzMkmmpOOFjQwEMAy1dvFMbm4LHlewQJAC/ksDBaUcQHHqjktCtrUb8rVjAyW +A8lscckeB2fEYyG5J6dJVaY4ClNOOs5yMDS2Afk1F6H/xKvtQ/5CzInA/QJATDub +K+BPU8jO9q+gpuIi3VIZdupssVGmCgObVCHLakG4uO04y9IyPhV9lA9tALtoIf4c +VIvv5fWGXBrZ48kZAQJBAJmVCdzQxd9LZI5vxijUCj5EI4e+x5DRqVUvyP8KCZrC +AiNyoDP85T+hBZaSXK3aYGpVwelyj3bvo1GrTNwNWLw= +-----END RSA PRIVATE KEY----- diff --git a/tests/auto/network/access/qnetworkreply/certs/server.pem b/tests/auto/network/access/qnetworkreply/certs/server.pem new file mode 100644 index 0000000000..67eb495319 --- /dev/null +++ b/tests/auto/network/access/qnetworkreply/certs/server.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEEzCCAvugAwIBAgIBADANBgkqhkiG9w0BAQUFADCBnDELMAkGA1UEBhMCTk8x +DTALBgNVBAgTBE9zbG8xEDAOBgNVBAcTB055ZGFsZW4xFjAUBgNVBAoTDVRyb2xs +dGVjaCBBU0ExFDASBgNVBAsTC0RldmVsb3BtZW50MRcwFQYDVQQDEw5mbHVrZS50 +cm9sbC5ubzElMCMGCSqGSIb3DQEJARYWYWhhbnNzZW5AdHJvbGx0ZWNoLmNvbTAe +Fw0wNzEyMDQwMTEwMzJaFw0zNTA0MjEwMTEwMzJaMGMxCzAJBgNVBAYTAk5PMQ0w +CwYDVQQIEwRPc2xvMRYwFAYDVQQKEw1Ucm9sbHRlY2ggQVNBMRQwEgYDVQQLEwtE +ZXZlbG9wbWVudDEXMBUGA1UEAxMOZmx1a2UudHJvbGwubm8wgZ8wDQYJKoZIhvcN +AQEBBQADgY0AMIGJAoGBAKfIoErEGQUbZroy4tLxHG8XguQ5LgFRkNsENDIRIcIN +b1nYU5BUP4OPqdOz1e4am4CuwyXJXqWvS2AFqqDRkQEfygSD41gcmTJFhHByWAOY +SmOLQfUISdKRAmBr5GT+3aCqdAjpNEyRXxI9N01ULK1/W5hgNgKMO/ZF8ydqm5Sd +AgMBAAGjggEaMIIBFjAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NM +IEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUIYUEPSMBZuX3nxqEJIqv +Cnn05awwgbsGA1UdIwSBszCBsKGBoqSBnzCBnDELMAkGA1UEBhMCTk8xDTALBgNV +BAgTBE9zbG8xEDAOBgNVBAcTB055ZGFsZW4xFjAUBgNVBAoTDVRyb2xsdGVjaCBB +U0ExFDASBgNVBAsTC0RldmVsb3BtZW50MRcwFQYDVQQDEw5mbHVrZS50cm9sbC5u +bzElMCMGCSqGSIb3DQEJARYWYWhhbnNzZW5AdHJvbGx0ZWNoLmNvbYIJAI6otOiR +t1QuMA0GCSqGSIb3DQEBBQUAA4IBAQBtV1/RBUPwYgXsKnGl3BkI8sSmvbsl2cqJ +AQ7kzx/BjMgkGDVTWXvAQ7Qy5piypu8VBQtIX+GgDJepoXfYNRgwvKmP07dUx/Gp +nl3mGb/2PFsr2OQ+YhiIi9Mk4UCbDOYpFmKr6gUkcDaqVZPvAoEbIxCiBOtWlXX8 ++JSxXULFPzZEhV06LpBGiqK5b4euDBVAGTGQ/Dslu67xZhMNhZDZSTSP8l35ettN +XSf2dp01jAamTKOxsrZvHdejAP1y657qRKGvITR9x0LiSZEZi8CtuoKAqHFw9DUx +kWOEIJXpYK9ki8z/PYp2dD3IVW3kjsMrHOhCGK6f5mucNAbsavLD +-----END CERTIFICATE----- diff --git a/tests/auto/network/access/qnetworkreply/echo/echo.pro b/tests/auto/network/access/qnetworkreply/echo/echo.pro new file mode 100644 index 0000000000..74b0bfcab3 --- /dev/null +++ b/tests/auto/network/access/qnetworkreply/echo/echo.pro @@ -0,0 +1,6 @@ +SOURCES += main.cpp +QT = core +CONFIG -= app_bundle debug_and_release_target +CONFIG += console + +symbian:TARGET.CAPABILITY="ALL -TCB" diff --git a/tests/auto/network/access/qnetworkreply/echo/main.cpp b/tests/auto/network/access/qnetworkreply/echo/main.cpp new file mode 100644 index 0000000000..a4ddda171a --- /dev/null +++ b/tests/auto/network/access/qnetworkreply/echo/main.cpp @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include + +int main(int argc, char **) +{ + if (argc < 2) { + printf("usage: echo\n"); + printf("echos all its input to its output.\n"); + return 1; + } + + QFile file; + file.open(stdin, QFile::ReadWrite); + QByteArray data = file.readAll(); + file.close(); + + file.open(stdout, QFile::WriteOnly); + file.write(data); + file.close(); + return 0; +} diff --git a/tests/auto/network/access/qnetworkreply/empty b/tests/auto/network/access/qnetworkreply/empty new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/auto/network/access/qnetworkreply/image1.jpg b/tests/auto/network/access/qnetworkreply/image1.jpg new file mode 100644 index 0000000000..dba31c1901 Binary files /dev/null and b/tests/auto/network/access/qnetworkreply/image1.jpg differ diff --git a/tests/auto/network/access/qnetworkreply/image2.jpg b/tests/auto/network/access/qnetworkreply/image2.jpg new file mode 100644 index 0000000000..72936e2de6 Binary files /dev/null and b/tests/auto/network/access/qnetworkreply/image2.jpg differ diff --git a/tests/auto/network/access/qnetworkreply/image3.jpg b/tests/auto/network/access/qnetworkreply/image3.jpg new file mode 100644 index 0000000000..cede519938 Binary files /dev/null and b/tests/auto/network/access/qnetworkreply/image3.jpg differ diff --git a/tests/auto/network/access/qnetworkreply/qnetworkreply.pro b/tests/auto/network/access/qnetworkreply/qnetworkreply.pro new file mode 100644 index 0000000000..86d3155c05 --- /dev/null +++ b/tests/auto/network/access/qnetworkreply/qnetworkreply.pro @@ -0,0 +1,7 @@ +TEMPLATE = subdirs +SUBDIRS = test + +requires(contains(QT_CONFIG,private_tests)) + +!wince*:SUBDIRS += echo +symbian: TARGET.CAPABILITY = NetworkServices diff --git a/tests/auto/network/access/qnetworkreply/qnetworkreply.qrc b/tests/auto/network/access/qnetworkreply/qnetworkreply.qrc new file mode 100644 index 0000000000..85ca6312af --- /dev/null +++ b/tests/auto/network/access/qnetworkreply/qnetworkreply.qrc @@ -0,0 +1,5 @@ + + + resource + + diff --git a/tests/auto/network/access/qnetworkreply/resource b/tests/auto/network/access/qnetworkreply/resource new file mode 100644 index 0000000000..e9b146042d --- /dev/null +++ b/tests/auto/network/access/qnetworkreply/resource @@ -0,0 +1,283 @@ + + + + + + +Network Working Group L. Masinter +Request for Comments: 2397 Xerox Corporation +Category: Standards Track August 1998 + + + The "data" URL scheme + +Status of this Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (1998). All Rights Reserved. + +1. Abstract + + A new URL scheme, "data", is defined. It allows inclusion of small + data items as "immediate" data, as if it had been included + externally. + +2. Description + + Some applications that use URLs also have a need to embed (small) + media type data directly inline. This document defines a new URL + scheme that would work like 'immediate addressing'. The URLs are of + the form: + + data:[][;base64], + + The is an Internet media type specification (with + optional parameters.) The appearance of ";base64" means that the data + is encoded as base64. Without ";base64", the data (as a sequence of + octets) is represented using ASCII encoding for octets inside the + range of safe URL characters and using the standard %xx hex encoding + of URLs for octets outside that range. If is omitted, it + defaults to text/plain;charset=US-ASCII. As a shorthand, + "text/plain" can be omitted but the charset parameter supplied. + + The "data:" URL scheme is only useful for short values. Note that + some applications that use URLs may impose a length limit; for + example, URLs embedded within anchors in HTML have a length limit + determined by the SGML declaration for HTML [RFC1866]. The LITLEN + (1024) limits the number of characters which can appear in a single + + + +Masinter Standards Track [Page 1] + +RFC 2397 The "data" URL scheme August 1998 + + + attribute value literal, the ATTSPLEN (2100) limits the sum of all + lengths of all attribute value specifications which appear in a tag, + and the TAGLEN (2100) limits the overall length of a tag. + + The "data" URL scheme has no relative URL forms. + +3. Syntax + + dataurl := "data:" [ mediatype ] [ ";base64" ] "," data + mediatype := [ type "/" subtype ] *( ";" parameter ) + data := *urlchar + parameter := attribute "=" value + + where "urlchar" is imported from [RFC2396], and "type", "subtype", + "attribute" and "value" are the corresponding tokens from [RFC2045], + represented using URL escaped encoding of [RFC2396] as necessary. + + Attribute values in [RFC2045] are allowed to be either represented as + tokens or as quoted strings. However, within a "data" URL, the + "quoted-string" representation would be awkward, since the quote mark + is itself not a valid urlchar. For this reason, parameter values + should use the URL Escaped encoding instead of quoted string if the + parameter values contain any "tspecial". + + The ";base64" extension is distinguishable from a content-type + parameter by the fact that it doesn't have a following "=" sign. + +4. Examples + + A data URL might be used for arbitrary types of data. The URL + + data:,A%20brief%20note + + encodes the text/plain string "A brief note", which might be useful + in a footnote link. + + The HTML fragment: + + Larry + + + + +Masinter Standards Track [Page 2] + +RFC 2397 The "data" URL scheme August 1998 + + + could be used for a small inline image in a HTML document. (The + embedded image is probably near the limit of utility. For anything + else larger, data URLs are likely to be inappropriate.) + + A data URL scheme's media type specification can include other + parameters; for example, one might specify a charset parameter. + + data:text/plain;charset=iso-8859-7,%be%fg%be + + can be used for a short sequence of greek characters. + + Some applications may use the "data" URL scheme in order to provide + setup parameters for other kinds of networking applications. For + example, one might create a media type + application/vnd-xxx-query + + whose content consists of a query string and a database identifier + for the "xxx" vendor's databases. A URL of the form: + + data:application/vnd-xxx- + query,select_vcount,fcol_from_fieldtable/local + + could then be used in a local application to launch the "helper" for + application/vnd-xxx-query and give it the immediate data included. + +5. History + + This idea was originally proposed August 1995. Some versions of the + data URL scheme have been used in the definition of VRML, and a + version has appeared as part of a proposal for embedded data in HTML. + Various changes have been made, based on requests, to elide the media + type, pack the indication of the base64 encoding more tightly, and + eliminate "quoted printable" as an encoding since it would not easily + yield valid URLs without additional %xx encoding, which itself is + sufficient. The "data" URL scheme is in use in VRML, new applications + of HTML, and various commercial products. It is being used for object + parameters in Java and ActiveX applications. + +6. Security + + Interpretation of the data within a "data" URL has the same security + considerations as any implementation of the given media type. An + application should not interpret the contents of a data URL which is + marked with a media type that has been disallowed for processing by + the application's configuration. + + + + + + +Masinter Standards Track [Page 3] + +RFC 2397 The "data" URL scheme August 1998 + + + Sites which use firewall proxies to disallow the retrieval of certain + media types (such as application script languages or types with known + security problems) will find it difficult to screen against the + inclusion of such types using the "data" URL scheme. However, they + should be aware of the threat and take whatever precautions are + considered necessary within their domain. + + The effect of using long "data" URLs in applications is currently + unknown; some software packages may exhibit unreasonable behavior + when confronted with data that exceeds its allocated buffer size. + +7. References + + [RFC2396] Berners-Lee, T., Fielding, R., and L. Masinter, + "Uniform Resource Identifiers (URI): Generic Syntax", RFC + 2396, August 1998. + + [RFC1866] Berners-Lee, T., and D. Connolly, "Hypertext Markup + Language - 2.0.", RFC 1866, November 1995. + + [RFC2045] Freed N., and N. Borenstein., "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + +Author contact information: + + Larry Masinter + Xerox Palo Alto Research Center + 3333 Coyote Hill Road + Palo Alto, CA 94304 + + EMail: masinter@parc.xerox.com + + + + + + + + + + + + + + + + + + + +Masinter Standards Track [Page 4] + +RFC 2397 The "data" URL scheme August 1998 + + +Full Copyright Statement + + Copyright (C) The Internet Society (1998). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + + + + + + + + + + + + + + + + + + + + + + + + +Masinter Standards Track [Page 5] + diff --git a/tests/auto/network/access/qnetworkreply/rfc3252.txt b/tests/auto/network/access/qnetworkreply/rfc3252.txt new file mode 100644 index 0000000000..b80c61bf0a --- /dev/null +++ b/tests/auto/network/access/qnetworkreply/rfc3252.txt @@ -0,0 +1,899 @@ + + + + + + +Network Working Group H. Kennedy +Request for Comments: 3252 Mimezine +Category: Informational 1 April 2002 + + + Binary Lexical Octet Ad-hoc Transport + +Status of this Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2002). All Rights Reserved. + +Abstract + + This document defines a reformulation of IP and two transport layer + protocols (TCP and UDP) as XML applications. + +1. Introduction + +1.1. Overview + + This document describes the Binary Lexical Octet Ad-hoc Transport + (BLOAT): a reformulation of a widely-deployed network-layer protocol + (IP [RFC791]), and two associated transport layer protocols (TCP + [RFC793] and UDP [RFC768]) as XML [XML] applications. It also + describes methods for transporting BLOAT over Ethernet and IEEE 802 + networks as well as encapsulating BLOAT in IP for gatewaying BLOAT + across the public Internet. + +1.2. Motivation + + The wild popularity of XML as a basis for application-level protocols + such as the Blocks Extensible Exchange Protocol [RFC3080], the Simple + Object Access Protocol [SOAP], and Jabber [JABBER] prompted + investigation into the possibility of extending the use of XML in the + protocol stack. Using XML at both the transport and network layer in + addition to the application layer would provide for an amazing amount + of power and flexibility while removing dependencies on proprietary + and hard-to-understand binary protocols. This protocol unification + would also allow applications to use a single XML parser for all + aspects of their operation, eliminating developer time spent figuring + out the intricacies of each new protocol, and moving the hard work of + + + + +Kennedy Informational [Page 1] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + parsing to the XML toolset. The use of XML also mitigates concerns + over "network vs. host" byte ordering which is at the root of many + network application bugs. + +1.3. Relation to Existing Protocols + + The reformulations specified in this RFC follow as closely as + possible the spirit of the RFCs on which they are based, and so MAY + contain elements or attributes that would not be needed in a pure + reworking (e.g. length attributes, which are implicit in XML.) + + The layering of network and transport protocols are maintained in + this RFC despite the optimizations that could be made if the line + were somewhat blurred (i.e. merging TCP and IP into a single, larger + element in the DTD) in order to foster future use of this protocol as + a basis for reformulating other protocols (such as ICMP.) + + Other than the encoding, the behavioral aspects of each of the + existing protocols remain unchanged. Routing, address spaces, TCP + congestion control, etc. behave as specified in the extant standards. + Adapting to new standards and experimental algorithm heuristics for + improving performance will become much easier once the move to BLOAT + has been completed. + +1.4. Requirement Levels + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in BCP 14, RFC 2119 + [RFC2119]. + +2. IPoXML + + This protocol MUST be implemented to be compliant with this RFC. + IPoXML is the root protocol REQUIRED for effective use of TCPoXML + (section 3.) and higher-level application protocols. + + The DTD for this document type can be found in section 7.1. + + The routing of IPoXML can be easily implemented on hosts with an XML + parser, as the regular structure lends itself handily to parsing and + validation of the document/datagram and then processing the + destination address, TTL, and checksum before sending it on to its + next-hop. + + The reformulation of IPv4 was chosen over IPv6 [RFC2460] due to the + wider deployment of IPv4 and the fact that implementing IPv6 as XML + would have exceeded the 1500 byte Ethernet MTU. + + + +Kennedy Informational [Page 2] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + All BLOAT implementations MUST use - and specify - the UTF-8 encoding + of RFC 2279 [RFC2279]. All BLOAT document/datagrams MUST be well- + formed and include the XMLDecl. + +2.1. IP Description + + A number of items have changed (for the better) from the original IP + specification. Bit-masks, where present have been converted into + human-readable values. IP addresses are listed in their dotted- + decimal notation [RFC1123]. Length and checksum values are present + as decimal integers. + + To calculate the length and checksum fields of the IP element, a + canonicalized form of the element MUST be used. The canonical form + SHALL have no whitespace (including newline characters) between + elements and only one space character between attributes. There + SHALL NOT be a space following the last attribute in an element. + + An iterative method SHOULD be used to calculate checksums, as the + length field will vary based on the size of the checksum. + + The payload element bears special attention. Due to the character + set restrictions of XML, the payload of IP datagrams (which MAY + contain arbitrary data) MUST be encoded for transport. This RFC + REQUIRES the contents of the payload to be encoded in the base-64 + encoding of RFC 2045 [RFC2045], but removes the requirement that the + encoded output MUST be wrapped on 76-character lines. + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 3] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +2.2. Example Datagram + + The following is an example IPoXML datagram with an empty payload: + + + + +
+ + + + + + + + + + + + + + + +
+ + +
+ +3. TCPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.2. + +3.1. TCP Description + + A number of items have changed from the original TCP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + To calculate the length and checksum fields of the TCP element, a + canonicalized form of the element MUST be used as in section 2.1. + + An iterative method SHOULD be used to calculate checksums as in + section 2.1. + + The payload element MUST be encoded as in section 2.1. + + + +Kennedy Informational [Page 4] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + The TCP offset element was expanded to a maximum of 255 from 16 to + allow for the increased size of the header in XML. + + TCPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +3.2. Example Datagram + + The following is an example TCPoXML datagram with an empty payload: + + + + + + + + + + + + + + + + + + + + + + + + +4. UDPoXML + + This protocol MUST be implemented to be compliant with this RFC. The + DTD for this document type can be found in section 7.3. + +4.1. UDP Description + + A number of items have changed from the original UDP specification. + Bit-masks, where present have been converted into human-readable + values. Length and checksum and port values are present as decimal + integers. + + + + + + + +Kennedy Informational [Page 5] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + To calculate the length and checksum fields of the UDP element, a + canonicalized form of the element MUST be used as in section 2.1. An + iterative method SHOULD be used to calculate checksums as in section + 2.1. + + The payload element MUST be encoded as in section 2.1. + + UDPoXML datagrams encapsulated by IPoXML MAY omit the header + as well as the declaration. + +4.2. Example Datagram + + The following is an example UDPoXML datagram with an empty payload: + + + + + + + + + + + + + + +5. Network Transport + + This document provides for the transmission of BLOAT datagrams over + two common families of physical layer transport. Future RFCs will + address additional transports as routing vendors catch up to the + specification, and we begin to see BLOAT routed across the Internet + backbone. + +5.1. Ethernet + + BLOAT is encapsulated in Ethernet datagrams as in [RFC894] with the + exception that the type field of the Ethernet frame MUST contain the + value 0xBEEF. The first 5 octets of the Ethernet frame payload will + be 0x3c 3f 78 6d 6c (" + --> + + + + +Kennedy Informational [Page 7] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 9] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 10] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + +7.2. TCPoXML DTD + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 11] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 12] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + + + + + + + + + + + + + + + + + + +7.3. UDPoXML DTD + + + + + + + + + + + + + + + +Kennedy Informational [Page 13] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +8. Security Considerations + + XML, as a subset of SGML, has the same security considerations as + specified in SGML Media Types [RFC1874]. Security considerations + that apply to IP, TCP and UDP also likely apply to BLOAT as it does + not attempt to correct for issues not related to message format. + +9. References + + [JABBER] Miller, J., "Jabber", draft-miller-jabber-00.txt, + February 2002. (Work in Progress) + + [RFC768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, + August 1980. + + [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791, + September 1981. + + [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC + 793, September 1981. + + [RFC894] Hornig, C., "Standard for the Transmission of IP + Datagrams over Ethernet Networks.", RFC 894, April 1984. + + [RFC1042] Postel, J. and J. Reynolds, "Standard for the + Transmission of IP Datagrams Over IEEE 802 Networks", STD + 43, RFC 1042, February 1988. + + [RFC1123] Braden, R., "Requirements for Internet Hosts - + Application and Support", RFC 1123, October 1989. + + [RFC1874] Levinson, E., "SGML Media Types", RFC 1874, December + 1995. + + [RFC2003] Perkins, C., "IP Encapsulation within IP", RFC 2003, + October 1996. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", RFC 2279, January 1998. + + + + + +Kennedy Informational [Page 14] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + + [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6 + (IPv6) Specification", RFC 2460, December 1998. + + [RFC3080] Rose, M., "The Blocks Extensible Exchange Protocol Core", + RFC 3080, March 2001. + + [SOAP] Box, D., Ehnebuske, D., Kakivaya, G., Layman, A., + Mendelsohn, N., Nielsen, H. F., Thatte, S. Winer, D., + "Simple Object Access Protocol (SOAP) 1.1" World Wide Web + Consortium Note, May 2000 http://www.w3.org/TR/SOAP/ + + [XML] Bray, T., Paoli, J., Sperberg-McQueen, C. M., "Extensible + Markup Language (XML)" World Wide Web Consortium + Recommendation REC- xml-19980210. + http://www.w3.org/TR/1998/REC-xml-19980210 + +10. Author's Address + + Hugh Kennedy + Mimezine + 1060 West Addison + Chicago, IL 60613 + USA + + EMail: kennedyh@engin.umich.edu + + + + + + + + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 15] + +RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002 + + +11. Full Copyright Statement + + Copyright (C) The Internet Society (2002). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Kennedy Informational [Page 16] + diff --git a/tests/auto/network/access/qnetworkreply/smb-file.txt b/tests/auto/network/access/qnetworkreply/smb-file.txt new file mode 100644 index 0000000000..73c3ac2c07 --- /dev/null +++ b/tests/auto/network/access/qnetworkreply/smb-file.txt @@ -0,0 +1 @@ +This is 34 bytes. Do not change... \ No newline at end of file diff --git a/tests/auto/network/access/qnetworkreply/test/test.pro b/tests/auto/network/access/qnetworkreply/test/test.pro new file mode 100644 index 0000000000..dba1c69dbb --- /dev/null +++ b/tests/auto/network/access/qnetworkreply/test/test.pro @@ -0,0 +1,38 @@ +load(qttest_p4) +QT -= gui +SOURCES += ../tst_qnetworkreply.cpp +TARGET = ../tst_qnetworkreply + +qpa:contains(QT_CONFIG,xcb): CONFIG+=insignificant_test # unstable, QTBUG-21102 + +win32 { + CONFIG(debug, debug|release) { + TARGET = ../../debug/tst_qnetworkreply +} else { + TARGET = ../../release/tst_qnetworkreply + } +} + +!symbian:DEFINES += SRCDIR=\\\"$$PWD/..\\\" + +QT = core-private network-private +RESOURCES += ../qnetworkreply.qrc + +symbian|wince*:{ + # For cross compiled targets, reference data files need to be deployed + addFiles.files = ../empty ../rfc3252.txt ../resource ../bigfile ../*.jpg + addFiles.path = . + DEPLOYMENT += addFiles + + certFiles.files = ../certs + certFiles.path = . + DEPLOYMENT += certFiles +} + +symbian:{ + # Symbian toolchain does not support correct include semantics + INCLUDEPATH+=..\\..\\..\\..\\include\\QtNetwork\\private + # bigfile test case requires more heap + TARGET.EPOCHEAPSIZE="0x100 0x10000000" + TARGET.CAPABILITY="ALL -TCB" +} diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp new file mode 100644 index 0000000000..dd9fe8ee82 --- /dev/null +++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp @@ -0,0 +1,6342 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef QT_NO_OPENSSL +#include +#include +#endif +#ifndef QT_NO_BEARERMANAGEMENT +#include +#include +#include +#endif + +#include + +#include "private/qnetworkaccessmanager_p.h" + +#ifdef Q_OS_SYMBIAN +#define SRCDIR "." +#endif + +#include "../../../network-settings.h" + +Q_DECLARE_METATYPE(QSharedPointer) +Q_DECLARE_METATYPE(QNetworkReply*) +Q_DECLARE_METATYPE(QAuthenticator*) +Q_DECLARE_METATYPE(QNetworkProxy) +Q_DECLARE_METATYPE(QNetworkProxyQuery) +Q_DECLARE_METATYPE(QList) +Q_DECLARE_METATYPE(QNetworkReply::NetworkError) +Q_DECLARE_METATYPE(QBuffer*) +Q_DECLARE_METATYPE(QHttpMultiPart *) +Q_DECLARE_METATYPE(QList) // for multiparts +#ifndef QT_NO_OPENSSL +Q_DECLARE_METATYPE(QSslConfiguration) +#endif + +class QNetworkReplyPtr: public QSharedPointer +{ +public: + inline QNetworkReplyPtr(QNetworkReply *ptr = 0) + : QSharedPointer(ptr) + { } + + inline operator QNetworkReply *() const { return data(); } +}; + +class MyCookieJar; +class tst_QNetworkReply: public QObject +{ + Q_OBJECT + + struct ProxyData { + ProxyData(const QNetworkProxy &p, const QByteArray &t, bool auth) + : tag(t), proxy(p), requiresAuthentication(auth) + { } + QByteArray tag; + QNetworkProxy proxy; + bool requiresAuthentication; + }; + + static bool seedCreated; + static QString createUniqueExtension() { + if (!seedCreated) { + qsrand(QTime(0,0,0).msecsTo(QTime::currentTime()) + QCoreApplication::applicationPid()); + seedCreated = true; // not thread-safe, but who cares + } + QString s = QString("%1-%2-%3").arg(QTime(0,0,0).msecsTo(QTime::currentTime())).arg(QCoreApplication::applicationPid()).arg(qrand()); + return s; + }; + + QEventLoop *loop; + enum RunSimpleRequestReturn { Timeout = 0, Success, Failure }; + int returnCode; + QString testFileName; +#if !defined Q_OS_WIN + QString wronlyFileName; +#endif + QString uniqueExtension; + QList proxies; + QNetworkAccessManager manager; + MyCookieJar *cookieJar; +#ifndef QT_NO_OPENSSL + QSslConfiguration storedSslConfiguration; + QList storedExpectedSslErrors; +#endif +#ifndef QT_NO_BEARERMANAGEMENT + QNetworkConfigurationManager *netConfMan; + QNetworkConfiguration networkConfiguration; + QScopedPointer networkSession; +#endif + +public: + tst_QNetworkReply(); + ~tst_QNetworkReply(); + QString runSimpleRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &request, + QNetworkReplyPtr &reply, const QByteArray &data = QByteArray()); + QString runMultipartRequest(const QNetworkRequest &request, QNetworkReplyPtr &reply, + QHttpMultiPart *multiPart, const QByteArray &verb); + + QString runCustomRequest(const QNetworkRequest &request, QNetworkReplyPtr &reply, + const QByteArray &verb, QIODevice *data); + +public Q_SLOTS: + void finished(); + void gotError(); + void authenticationRequired(QNetworkReply*,QAuthenticator*); + void proxyAuthenticationRequired(const QNetworkProxy &,QAuthenticator*); + +#ifndef QT_NO_OPENSSL + void sslErrors(QNetworkReply*,const QList &); + void storeSslConfiguration(); + void ignoreSslErrorListSlot(QNetworkReply *reply, const QList &); +#endif + +protected Q_SLOTS: + void nestedEventLoops_slot(); + +private Q_SLOTS: + void init(); + void cleanup(); + void initTestCase(); + void cleanupTestCase(); + + void stateChecking(); + void invalidProtocol(); + void getFromData_data(); + void getFromData(); + void getFromFile(); + void getFromFileSpecial_data(); + void getFromFileSpecial(); + void getFromFtp_data(); + void getFromFtp(); + void getFromHttp_data(); + void getFromHttp(); + void getErrors_data(); + void getErrors(); + void putToFile_data(); + void putToFile(); + void putToFtp_data(); + void putToFtp(); + void putToHttp_data(); + void putToHttp(); + void putToHttpSynchronous_data(); + void putToHttpSynchronous(); + void putToHttpMultipart_data(); + void putToHttpMultipart(); + void postToHttp_data(); + void postToHttp(); + void postToHttpSynchronous_data(); + void postToHttpSynchronous(); + void postToHttpMultipart_data(); + void postToHttpMultipart(); + void deleteFromHttp_data(); + void deleteFromHttp(); + void putGetDeleteGetFromHttp_data(); + void putGetDeleteGetFromHttp(); + void sendCustomRequestToHttp_data(); + void sendCustomRequestToHttp(); + void connectToIPv6Address_data(); + void connectToIPv6Address(); + + void ioGetFromData_data(); + void ioGetFromData(); + void ioGetFromFileSpecial_data(); + void ioGetFromFileSpecial(); + void ioGetFromFile_data(); + void ioGetFromFile(); + void ioGetFromFtp_data(); + void ioGetFromFtp(); + void ioGetFromFtpWithReuse(); + void ioGetFromHttp(); + + void ioGetFromBuiltinHttp_data(); + void ioGetFromBuiltinHttp(); + void ioGetFromHttpWithReuseParallel(); + void ioGetFromHttpWithReuseSequential(); + void ioGetFromHttpWithAuth_data(); + void ioGetFromHttpWithAuth(); + void ioGetFromHttpWithAuthSynchronous(); + void ioGetFromHttpWithProxyAuth(); + void ioGetFromHttpWithProxyAuthSynchronous(); + void ioGetFromHttpWithSocksProxy(); +#ifndef QT_NO_OPENSSL + void ioGetFromHttpsWithSslErrors(); + void ioGetFromHttpsWithIgnoreSslErrors(); + void ioGetFromHttpsWithSslHandshakeError(); +#endif + void ioGetFromHttpBrokenServer_data(); + void ioGetFromHttpBrokenServer(); + void ioGetFromHttpStatus100_data(); + void ioGetFromHttpStatus100(); + void ioGetFromHttpNoHeaders_data(); + void ioGetFromHttpNoHeaders(); + void ioGetFromHttpWithCache_data(); + void ioGetFromHttpWithCache(); + + void ioGetWithManyProxies_data(); + void ioGetWithManyProxies(); + + void ioPutToFileFromFile_data(); + void ioPutToFileFromFile(); + void ioPutToFileFromSocket_data(); + void ioPutToFileFromSocket(); + void ioPutToFileFromLocalSocket_data(); + void ioPutToFileFromLocalSocket(); + void ioPutToFileFromProcess_data(); + void ioPutToFileFromProcess(); + void ioPutToFtpFromFile_data(); + void ioPutToFtpFromFile(); + void ioPutToHttpFromFile_data(); + void ioPutToHttpFromFile(); + void ioPostToHttpFromFile_data(); + void ioPostToHttpFromFile(); + void ioPostToHttpFromSocket_data(); + void ioPostToHttpFromSocket(); + void ioPostToHttpFromSocketSynchronous(); + void ioPostToHttpFromSocketSynchronous_data(); + void ioPostToHttpFromMiddleOfFileToEnd(); + void ioPostToHttpFromMiddleOfFileFiveBytes(); + void ioPostToHttpFromMiddleOfQBufferFiveBytes(); + void ioPostToHttpNoBufferFlag(); + void ioPostToHttpUploadProgress(); + void ioPostToHttpEmptyUploadProgress(); + + void lastModifiedHeaderForFile(); + void lastModifiedHeaderForHttp(); + + void httpCanReadLine(); + + void rateControl_data(); + void rateControl(); + + void downloadProgress_data(); + void downloadProgress(); + void uploadProgress_data(); + void uploadProgress(); + + void chaining_data(); + void chaining(); + + void receiveCookiesFromHttp_data(); + void receiveCookiesFromHttp(); + void receiveCookiesFromHttpSynchronous_data(); + void receiveCookiesFromHttpSynchronous(); + void sendCookies_data(); + void sendCookies(); + void sendCookiesSynchronous_data(); + void sendCookiesSynchronous(); + + void nestedEventLoops(); + + void httpProxyCommands_data(); + void httpProxyCommands(); + void httpProxyCommandsSynchronous_data(); + void httpProxyCommandsSynchronous(); + void proxyChange(); + void authorizationError_data(); + void authorizationError(); + + void httpConnectionCount(); + + void httpReUsingConnectionSequential_data(); + void httpReUsingConnectionSequential(); + void httpReUsingConnectionFromFinishedSlot_data(); + void httpReUsingConnectionFromFinishedSlot(); + + void httpRecursiveCreation(); + +#ifndef QT_NO_OPENSSL + void ioPostToHttpsUploadProgress(); + void ignoreSslErrorsList_data(); + void ignoreSslErrorsList(); + void ignoreSslErrorsListWithSlot_data(); + void ignoreSslErrorsListWithSlot(); + void sslConfiguration_data(); + void sslConfiguration(); +#endif + + void getAndThenDeleteObject_data(); + void getAndThenDeleteObject(); + + void symbianOpenCDataUrlCrash(); + + void getFromHttpIntoBuffer_data(); + void getFromHttpIntoBuffer(); + void getFromHttpIntoBuffer2_data(); + void getFromHttpIntoBuffer2(); + void getFromHttpIntoBufferCanReadLine(); + + void ioGetFromHttpWithoutContentLength(); + + void ioGetFromHttpBrokenChunkedEncoding(); + void qtbug12908compressedHttpReply(); + void compressedHttpReplyBrokenGzip(); + + void getFromUnreachableIp(); + + void qtbug4121unknownAuthentication(); + + void qtbug13431replyThrottling(); + + void httpWithNoCredentialUsage(); + + void qtbug15311doubleContentLength(); + + void qtbug18232gzipContentLengthZero(); + + void synchronousRequest_data(); + void synchronousRequest(); +#ifndef QT_NO_OPENSSL + void synchronousRequestSslFailure(); +#endif + + void httpAbort(); + + void dontInsertPartialContentIntoTheCache(); + + void httpUserAgent(); + + // NOTE: This test must be last! + void parentingRepliesToTheApp(); +}; + +bool tst_QNetworkReply::seedCreated = false; + +QT_BEGIN_NAMESPACE + +namespace QTest { + template<> + char *toString(const QNetworkReply::NetworkError& code) + { + const QMetaObject *mo = &QNetworkReply::staticMetaObject; + int index = mo->indexOfEnumerator("NetworkError"); + if (index == -1) + return qstrdup(""); + + QMetaEnum qme = mo->enumerator(index); + return qstrdup(qme.valueToKey(code)); + } + + template<> + char *toString(const QNetworkCookie &cookie) + { + return qstrdup(cookie.toRawForm()); + } + + template<> + char *toString(const QList &list) + { + QString result = "QList("; + bool first = true; + foreach (QNetworkCookie cookie, list) { + if (!first) + result += ", "; + first = false; + result += QString::fromLatin1("QNetworkCookie(%1)").arg(QLatin1String(cookie.toRawForm())); + } + + return qstrdup(result.append(')').toLocal8Bit()); + } +} + +QT_END_NAMESPACE + +#define RUN_REQUEST(call) \ + do { \ + QString errorMsg = call; \ + if (!errorMsg.isEmpty()) \ + QFAIL(qPrintable(errorMsg)); \ + } while (0); + +#ifndef QT_NO_OPENSSL +static void setupSslServer(QSslSocket* serverSocket) +{ + serverSocket->setProtocol(QSsl::AnyProtocol); + serverSocket->setLocalCertificate(SRCDIR "/certs/server.pem"); + serverSocket->setPrivateKey(SRCDIR "/certs/server.key"); +} +#endif + +// Does not work for POST/PUT! +class MiniHttpServer: public QTcpServer +{ + Q_OBJECT +public: + QTcpSocket *client; // always the last one that was received + QByteArray dataToTransmit; + QByteArray receivedData; + QSemaphore ready; + bool doClose; + bool doSsl; + bool ipv6; + bool multiple; + int totalConnections; + + MiniHttpServer(const QByteArray &data, bool ssl = false, QThread *thread = 0, bool useipv6 = false) + : client(0), dataToTransmit(data), doClose(true), doSsl(ssl), ipv6(useipv6), + multiple(false), totalConnections(0) + { + if (useipv6) { + listen(QHostAddress::AnyIPv6); + } else { + listen(); + } + if (thread) { + connect(thread, SIGNAL(started()), this, SLOT(threadStartedSlot())); + moveToThread(thread); + thread->start(); + ready.acquire(); + } + } + +protected: + void incomingConnection(int socketDescriptor) + { + //qDebug() << "incomingConnection" << socketDescriptor << "doSsl:" << doSsl << "ipv6:" << ipv6; + if (!doSsl) { + client = new QTcpSocket; + client->setSocketDescriptor(socketDescriptor); + connectSocketSignals(); + } else { +#ifndef QT_NO_OPENSSL + QSslSocket *serverSocket = new QSslSocket; + serverSocket->setParent(this); + if (serverSocket->setSocketDescriptor(socketDescriptor)) { + connect(serverSocket, SIGNAL(sslErrors(QList)), this, SLOT(slotSslErrors(QList))); + setupSslServer(serverSocket); + serverSocket->startServerEncryption(); + client = serverSocket; + connectSocketSignals(); + } else { + delete serverSocket; + return; + } +#endif + } + client->setParent(this); + ++totalConnections; + } +private: + void connectSocketSignals() + { + //qDebug() << "connectSocketSignals" << client; + connect(client, SIGNAL(readyRead()), this, SLOT(readyReadSlot())); + connect(client, SIGNAL(bytesWritten(qint64)), this, SLOT(bytesWrittenSlot())); + connect(client, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(slotError(QAbstractSocket::SocketError))); + } + +private slots: +#ifndef QT_NO_OPENSSL + void slotSslErrors(const QList& errors) + { + qDebug() << "slotSslErrors" << client->errorString() << errors; + } +#endif + void slotError(QAbstractSocket::SocketError err) + { + qDebug() << "slotError" << err << client->errorString(); + } + +public slots: + void readyReadSlot() + { + receivedData += client->readAll(); + int doubleEndlPos = receivedData.indexOf("\r\n\r\n"); + + if (doubleEndlPos != -1) { + // multiple requests incoming. remove the bytes of the current one + if (multiple) + receivedData.remove(0, doubleEndlPos+4); + + // we need to emulate the bytesWrittenSlot call if the data is empty. + if (dataToTransmit.size() == 0) + QMetaObject::invokeMethod(this, "bytesWrittenSlot", Qt::QueuedConnection); + else + client->write(dataToTransmit); + } + } + + void bytesWrittenSlot() { + if (doClose && client->bytesToWrite() == 0) { + client->disconnectFromHost(); + disconnect(client, 0, this, 0); + } + } + + void threadStartedSlot() + { + ready.release(); + } +}; + +class MyCookieJar: public QNetworkCookieJar +{ +public: + inline QList allCookies() const + { return QNetworkCookieJar::allCookies(); } + inline void setAllCookies(const QList &cookieList) + { QNetworkCookieJar::setAllCookies(cookieList); } +}; + +class MyProxyFactory: public QNetworkProxyFactory +{ +public: + int callCount; + QList toReturn; + QNetworkProxyQuery lastQuery; + inline MyProxyFactory() { clear(); } + + inline void clear() + { + callCount = 0; + toReturn = QList() << QNetworkProxy::DefaultProxy; + lastQuery = QNetworkProxyQuery(); + } + + virtual QList queryProxy(const QNetworkProxyQuery &query) + { + lastQuery = query; + ++callCount; + return toReturn; + } +}; + +class MyMemoryCache: public QAbstractNetworkCache +{ +public: + typedef QPair CachedContent; + typedef QHash CacheData; + CacheData cache; + + MyMemoryCache(QObject *parent) : QAbstractNetworkCache(parent) {} + + QNetworkCacheMetaData metaData(const QUrl &url) + { + return cache.value(url.toEncoded()).first; + } + + void updateMetaData(const QNetworkCacheMetaData &metaData) + { + cache[metaData.url().toEncoded()].first = metaData; + } + + QIODevice *data(const QUrl &url) + { + CacheData::ConstIterator it = cache.find(url.toEncoded()); + if (it == cache.constEnd()) + return 0; + QBuffer *io = new QBuffer(this); + io->setData(it->second); + io->open(QIODevice::ReadOnly); + io->seek(0); + return io; + } + + bool remove(const QUrl &url) + { + cache.remove(url.toEncoded()); + return true; + } + + qint64 cacheSize() const + { + qint64 total = 0; + foreach (const CachedContent &entry, cache) + total += entry.second.size(); + return total; + } + + QIODevice *prepare(const QNetworkCacheMetaData &) + { + qFatal("%s: Should not have tried to add to the cache", Q_FUNC_INFO); + return 0; + } + void insert(QIODevice *) + { + qFatal("%s: Should not have tried to add to the cache", Q_FUNC_INFO); + } + + void clear() { cache.clear(); } +}; +Q_DECLARE_METATYPE(MyMemoryCache::CachedContent) +Q_DECLARE_METATYPE(MyMemoryCache::CacheData) + +class MySpyMemoryCache: public QAbstractNetworkCache +{ +public: + MySpyMemoryCache(QObject *parent) : QAbstractNetworkCache(parent) {} + ~MySpyMemoryCache() + { + qDeleteAll(m_buffers); + m_buffers.clear(); + } + + QHash m_buffers; + QList m_insertedUrls; + + QNetworkCacheMetaData metaData(const QUrl &) + { + return QNetworkCacheMetaData(); + } + + void updateMetaData(const QNetworkCacheMetaData &) + { + } + + QIODevice *data(const QUrl &) + { + return 0; + } + + bool remove(const QUrl &url) + { + delete m_buffers.take(url); + return m_insertedUrls.removeAll(url) > 0; + } + + qint64 cacheSize() const + { + return 0; + } + + QIODevice *prepare(const QNetworkCacheMetaData &metaData) + { + QBuffer* buffer = new QBuffer; + buffer->open(QIODevice::ReadWrite); + buffer->setProperty("url", metaData.url()); + m_buffers.insert(metaData.url(), buffer); + return buffer; + } + + void insert(QIODevice *buffer) + { + QUrl url = buffer->property("url").toUrl(); + m_insertedUrls << url; + delete m_buffers.take(url); + } + + void clear() { m_insertedUrls.clear(); } +}; + +class DataReader: public QObject +{ + Q_OBJECT +public: + qint64 totalBytes; + QByteArray data; + QIODevice *device; + bool accumulate; + DataReader(QIODevice *dev, bool acc = true) : totalBytes(0), device(dev), accumulate(acc) + { + connect(device, SIGNAL(readyRead()), SLOT(doRead())); + } + +public slots: + void doRead() + { + QByteArray buffer; + buffer.resize(device->bytesAvailable()); + qint64 bytesRead = device->read(buffer.data(), device->bytesAvailable()); + if (bytesRead == -1) { + QTestEventLoop::instance().exitLoop(); + return; + } + buffer.truncate(bytesRead); + totalBytes += bytesRead; + + if (accumulate) + data += buffer; + } +}; + + +class SocketPair: public QObject +{ + Q_OBJECT +public: + QIODevice *endPoints[2]; + + SocketPair(QObject *parent = 0) + : QObject(parent) + { + endPoints[0] = endPoints[1] = 0; + } + + bool create() + { + QTcpServer server; + server.listen(); + + QTcpSocket *active = new QTcpSocket(this); + active->connectToHost("127.0.0.1", server.serverPort()); +#ifndef Q_OS_SYMBIAN + // need more time as working with embedded + // device and testing from emualtor + // things tend to get slower + if (!active->waitForConnected(1000)) + return false; + + if (!server.waitForNewConnection(1000)) + return false; +#else + if (!active->waitForConnected(100)) + return false; + + if (!server.waitForNewConnection(100)) + return false; +#endif + QTcpSocket *passive = server.nextPendingConnection(); + passive->setParent(this); + + endPoints[0] = active; + endPoints[1] = passive; + return true; + } +}; + +// A blocking tcp server (must be used in a thread) which supports SSL. +class BlockingTcpServer : public QTcpServer +{ + Q_OBJECT +public: + BlockingTcpServer(bool ssl) : doSsl(ssl), sslSocket(0) {} + + QTcpSocket* waitForNextConnectionSocket() { + waitForNewConnection(-1); + if (doSsl) { + if (!sslSocket) + qFatal("%s: sslSocket should not be null after calling waitForNewConnection()", + Q_FUNC_INFO); + return sslSocket; + } else { + //qDebug() << "returning nextPendingConnection"; + return nextPendingConnection(); + } + } + virtual void incomingConnection(int socketDescriptor) + { +#ifndef QT_NO_OPENSSL + if (doSsl) { + QSslSocket *serverSocket = new QSslSocket; + serverSocket->setParent(this); + serverSocket->setSocketDescriptor(socketDescriptor); + connect(serverSocket, SIGNAL(sslErrors(QList)), this, SLOT(slotSslErrors(QList))); + setupSslServer(serverSocket); + serverSocket->startServerEncryption(); + sslSocket = serverSocket; + } else +#endif + { + QTcpServer::incomingConnection(socketDescriptor); + } + } +private slots: + +#ifndef QT_NO_OPENSSL + void slotSslErrors(const QList& errors) + { + qDebug() << "slotSslErrors" << sslSocket->errorString() << errors; + } +#endif + +private: + const bool doSsl; + QTcpSocket* sslSocket; +}; + +// This server tries to send data as fast as possible (like most servers) +// but it measures how fast it was able to send it, which shows at which +// rate the reader is processing the data. +class FastSender: public QThread +{ + Q_OBJECT + QSemaphore ready; + qint64 wantedSize; + int port; + enum Protocol { DebugPipe, ProvidedData }; + const Protocol protocol; + const bool doSsl; + const bool fillKernelBuffer; +public: + int transferRate; + QWaitCondition cond; + + QByteArray dataToTransmit; + int dataIndex; + + // a server that sends debugpipe data + FastSender(qint64 size) + : wantedSize(size), port(-1), protocol(DebugPipe), + doSsl(false), fillKernelBuffer(true), transferRate(-1), + dataIndex(0) + { + start(); + ready.acquire(); + } + + // a server that sends the data provided at construction time, useful for HTTP + FastSender(const QByteArray& data, bool https, bool fillBuffer) + : wantedSize(data.size()), port(-1), protocol(ProvidedData), + doSsl(https), fillKernelBuffer(fillBuffer), transferRate(-1), + dataToTransmit(data), dataIndex(0) + { + start(); + ready.acquire(); + } + + inline int serverPort() const { return port; } + + int writeNextData(QTcpSocket* socket, qint32 size) + { + if (protocol == DebugPipe) { + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + stream << QVariantMap() << QByteArray(size, 'a'); + socket->write((char*)&size, sizeof size); + socket->write(data); + dataIndex += size; + return size; + } else { + const QByteArray data = dataToTransmit.mid(dataIndex, size); + socket->write(data); + dataIndex += data.size(); + //qDebug() << "wrote" << dataIndex << "/" << dataToTransmit.size(); + return data.size(); + } + } + void writeLastData(QTcpSocket* socket) + { + if (protocol == DebugPipe) { + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + stream << QVariantMap() << QByteArray(); + const qint32 size = data.size(); + socket->write((char*)&size, sizeof size); + socket->write(data); + } + } + +protected: + void run() + { + BlockingTcpServer server(doSsl); + server.listen(); + port = server.serverPort(); + ready.release(); + + QTcpSocket *client = server.waitForNextConnectionSocket(); + + // get the "request" packet + if (!client->waitForReadyRead(2000)) { + qDebug() << "FastSender:" << client->error() << "waiting for \"request\" packet"; + return; + } + client->readAll(); // we're not interested in the actual contents (e.g. HTTP request) + + enum { BlockSize = 1024 }; + + if (fillKernelBuffer) { + + // write a bunch of bytes to fill up the buffers + bool done = false; + do { + if (writeNextData(client, BlockSize) < BlockSize) { + qDebug() << "ERROR: FastSender: not enough data to write in order to fill buffers; or client is reading too fast"; + return; + } + while (client->bytesToWrite() > 0) { + if (!client->waitForBytesWritten(0)) { + done = true; + break; + } + } + //qDebug() << "Filling kernel buffer: wrote" << dataIndex << "bytes"; + } while (!done); + + qDebug() << "FastSender: ok, kernel buffer is full after writing" << dataIndex << "bytes"; + } + + // Tell the client to start reading + emit dataReady(); + + // the kernel buffer is full + // clean up QAbstractSocket's residue: + while (client->bytesToWrite() > 0) { + qDebug() << "Still having" << client->bytesToWrite() << "bytes to write, doing that now"; + if (!client->waitForBytesWritten(2000)) { + qDebug() << "ERROR: FastSender:" << client->error() << "cleaning up residue"; + return; + } + } + + // now write in "blocking mode", this is where the rate measuring starts + QTime timer; + timer.start(); + //const qint64 writtenBefore = dataIndex; + //qint64 measuredTotalBytes = wantedSize - writtenBefore; + qint64 measuredSentBytes = 0; + while (dataIndex < wantedSize) { + const int remainingBytes = wantedSize - measuredSentBytes; + const int bytesToWrite = qMin(remainingBytes, static_cast(BlockSize)); + if (bytesToWrite <= 0) + qFatal("%s: attempt to write %d bytes", Q_FUNC_INFO, bytesToWrite); + measuredSentBytes += writeNextData(client, bytesToWrite); + + while (client->bytesToWrite() > 0) { + if (!client->waitForBytesWritten(2000)) { + qDebug() << "ERROR: FastSender:" << client->error() << "during blocking write"; + return; + } + } + /*qDebug() << "FastSender:" << bytesToWrite << "bytes written now;" + << measuredSentBytes << "measured bytes" << measuredSentBytes + writtenBefore << "total (" + << measuredSentBytes*100/measuredTotalBytes << "% complete);" + << timer.elapsed() << "ms elapsed";*/ + } + + transferRate = measuredSentBytes * 1000 / timer.elapsed(); + qDebug() << "FastSender: flushed" << measuredSentBytes << "bytes in" << timer.elapsed() << "ms: rate =" << transferRate << "B/s"; + + // write a "close connection" packet, if the protocol needs it + writeLastData(client); + } +signals: + void dataReady(); +}; + +class RateControlledReader: public QObject +{ + Q_OBJECT + QIODevice *device; + int bytesToRead; + int interval; + int readBufferSize; +public: + QByteArray data; + qint64 totalBytesRead; + RateControlledReader(QObject& senderObj, QIODevice *dev, int kbPerSec, int maxBufferSize = 0) + : device(dev), readBufferSize(maxBufferSize), totalBytesRead(0) + { + // determine how often we have to wake up + int timesPerSecond; + if (readBufferSize == 0) { + // The requirement is simply "N KB per seconds" + timesPerSecond = 20; + bytesToRead = kbPerSec * 1024 / timesPerSecond; + } else { + // The requirement also includes " bytes at a time" + bytesToRead = readBufferSize; + timesPerSecond = kbPerSec * 1024 / readBufferSize; + } + interval = 1000 / timesPerSecond; // in ms + + qDebug() << "RateControlledReader: going to read" << bytesToRead + << "bytes every" << interval << "ms"; + qDebug() << "actual read rate will be" + << (bytesToRead * 1000 / interval) << "bytes/sec (wanted" + << kbPerSec * 1024 << "bytes/sec)"; + + // Wait for data to be readyRead + bool ok = connect(&senderObj, SIGNAL(dataReady()), this, SLOT(slotDataReady())); + if (!ok) + qFatal("%s: Cannot connect dataReady signal", Q_FUNC_INFO); + } + + void wrapUp() + { + QByteArray someData = device->read(device->bytesAvailable()); + data += someData; + totalBytesRead += someData.size(); + qDebug() << "wrapUp: found" << someData.size() << "bytes left. progress" << data.size(); + //qDebug() << "wrapUp: now bytesAvailable=" << device->bytesAvailable(); + } + +private slots: + void slotDataReady() + { + //qDebug() << "RateControlledReader: ready to go"; + startTimer(interval); + } + +protected: + void timerEvent(QTimerEvent *) + { + //qDebug() << "RateControlledReader: timerEvent bytesAvailable=" << device->bytesAvailable(); + if (readBufferSize > 0 && device->bytesAvailable() > readBufferSize) { + // This passes all the time, except in the final flush. + //qFatal("%s: Too many bytes available", Q_FUNC_INFO); + } + + qint64 bytesRead = 0; + QTime stopWatch; + stopWatch.start(); + do { + if (device->bytesAvailable() == 0) { + if (stopWatch.elapsed() > 20) { + qDebug() << "RateControlledReader: Not enough data available for reading, waited too much, timing out"; + break; + } + if (!device->waitForReadyRead(5)) { + qDebug() << "RateControlledReader: Not enough data available for reading, even after waiting 5ms, bailing out"; + break; + } + } + QByteArray someData = device->read(bytesToRead - bytesRead); + data += someData; + bytesRead += someData.size(); + //qDebug() << "RateControlledReader: successfully read" << someData.size() << "progress:" << data.size(); + } while (bytesRead < bytesToRead); + totalBytesRead += bytesRead; + + if (bytesRead < bytesToRead) + qWarning() << "RateControlledReader: WARNING:" << bytesToRead - bytesRead << "bytes not read"; + } +}; + + +tst_QNetworkReply::tst_QNetworkReply() +{ + qRegisterMetaType(); // for QSignalSpy + qRegisterMetaType(); + qRegisterMetaType(); +#ifndef QT_NO_OPENSSL + qRegisterMetaType >(); +#endif + qRegisterMetaType(); + + Q_SET_DEFAULT_IAP + + testFileName = QDir::currentPath() + "/testfile"; + uniqueExtension = createUniqueExtension(); + cookieJar = new MyCookieJar; + manager.setCookieJar(cookieJar); + + QHostInfo hostInfo = QHostInfo::fromName(QtNetworkSettings::serverName()); + + proxies << ProxyData(QNetworkProxy::NoProxy, "", false); + + if (hostInfo.error() == QHostInfo::NoError && !hostInfo.addresses().isEmpty()) { + QString proxyserver = hostInfo.addresses().first().toString(); + proxies << ProxyData(QNetworkProxy(QNetworkProxy::HttpProxy, proxyserver, 3128), "+proxy", false) + << ProxyData(QNetworkProxy(QNetworkProxy::HttpProxy, proxyserver, 3129), "+proxyauth", true) + // currently unsupported + // << ProxyData(QNetworkProxy(QNetworkProxy::HttpProxy, proxyserver, 3130), "+proxyauth-ntlm", true); + << ProxyData(QNetworkProxy(QNetworkProxy::Socks5Proxy, proxyserver, 1080), "+socks", false) + << ProxyData(QNetworkProxy(QNetworkProxy::Socks5Proxy, proxyserver, 1081), "+socksauth", true); + } else { + printf("==================================================================\n"); + printf("Proxy could not be looked up. No proxy will be used while testing!\n"); + printf("==================================================================\n"); + } +} + +tst_QNetworkReply::~tst_QNetworkReply() +{ +} + + +void tst_QNetworkReply::authenticationRequired(QNetworkReply*, QAuthenticator* auth) +{ + auth->setUser("httptest"); + auth->setPassword("httptest"); +} + +void tst_QNetworkReply::proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator* auth) +{ + auth->setUser("qsockstest"); + auth->setPassword("password"); +} + +#ifndef QT_NO_OPENSSL +void tst_QNetworkReply::sslErrors(QNetworkReply *reply, const QList &errors) +{ + reply->ignoreSslErrors(); + QVERIFY(!errors.isEmpty()); + QVERIFY(!reply->sslConfiguration().isNull()); +} + +void tst_QNetworkReply::storeSslConfiguration() +{ + storedSslConfiguration = QSslConfiguration(); + QNetworkReply *reply = qobject_cast(sender()); + if (reply) + storedSslConfiguration = reply->sslConfiguration(); +} +#endif + +QString tst_QNetworkReply::runMultipartRequest(const QNetworkRequest &request, + QNetworkReplyPtr &reply, + QHttpMultiPart *multiPart, + const QByteArray &verb) +{ + if (verb == "POST") + reply = manager.post(request, multiPart); + else + reply = manager.put(request, multiPart); + + // the code below is copied from tst_QNetworkReply::runSimpleRequest, see below + reply->setParent(this); + connect(reply, SIGNAL(finished()), SLOT(finished())); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(gotError())); + multiPart->setParent(reply); + + returnCode = Timeout; + loop = new QEventLoop; + QTimer::singleShot(25000, loop, SLOT(quit())); + int code = returnCode == Timeout ? loop->exec() : returnCode; + delete loop; + loop = 0; + + switch (code) { + case Failure: + return "Request failed: " + reply->errorString(); + case Timeout: + return "Network timeout"; + } + return QString(); +} + +QString tst_QNetworkReply::runSimpleRequest(QNetworkAccessManager::Operation op, + const QNetworkRequest &request, + QNetworkReplyPtr &reply, + const QByteArray &data) +{ + switch (op) { + case QNetworkAccessManager::HeadOperation: + reply = manager.head(request); + break; + + case QNetworkAccessManager::GetOperation: + reply = manager.get(request); + break; + + case QNetworkAccessManager::PutOperation: + reply = manager.put(request, data); + break; + + case QNetworkAccessManager::PostOperation: + reply = manager.post(request, data); + break; + + case QNetworkAccessManager::DeleteOperation: + reply = manager.deleteResource(request); + break; + + default: + qFatal("%s: Invalid/unknown operation requested", Q_FUNC_INFO); + } + reply->setParent(this); + + returnCode = Timeout; + int code = Success; + + if (request.attribute(QNetworkRequest::SynchronousRequestAttribute).toBool()) { + if (reply->isFinished()) + code = reply->error() != QNetworkReply::NoError ? Failure : Success; + else + code = Failure; + } else { + connect(reply, SIGNAL(finished()), SLOT(finished())); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(gotError())); + + loop = new QEventLoop; + QTimer::singleShot(20000, loop, SLOT(quit())); + code = returnCode == Timeout ? loop->exec() : returnCode; + delete loop; + loop = 0; + } + + switch (code) { + case Failure: + return "Request failed: " + reply->errorString(); + case Timeout: + return "Network timeout"; + } + return QString(); +} + +QString tst_QNetworkReply::runCustomRequest(const QNetworkRequest &request, + QNetworkReplyPtr &reply, + const QByteArray &verb, + QIODevice *data) +{ + reply = manager.sendCustomRequest(request, verb, data); + reply->setParent(this); + connect(reply, SIGNAL(finished()), SLOT(finished())); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(gotError())); + + returnCode = Timeout; + loop = new QEventLoop; + QTimer::singleShot(20000, loop, SLOT(quit())); + int code = returnCode == Timeout ? loop->exec() : returnCode; + delete loop; + loop = 0; + + switch (code) { + case Failure: + return "Request failed: " + reply->errorString(); + case Timeout: + return "Network timeout"; + } + return QString(); +} + +void tst_QNetworkReply::finished() +{ + loop->exit(returnCode = Success); +} + +void tst_QNetworkReply::gotError() +{ + loop->exit(returnCode = Failure); + disconnect(QObject::sender(), SIGNAL(finished()), this, 0); +} + +void tst_QNetworkReply::initTestCase() +{ +#if !defined Q_OS_WIN + wronlyFileName = QDir::currentPath() + "/write-only"; + QFile wr(wronlyFileName); + QVERIFY(wr.open(QIODevice::WriteOnly | QIODevice::Truncate)); + wr.setPermissions(QFile::WriteOwner | QFile::WriteUser); + wr.close(); +#endif + + QDir::setSearchPaths("srcdir", QStringList() << SRCDIR); +#ifndef QT_NO_OPENSSL + QSslSocket::defaultCaCertificates(); //preload certificates +#endif +#ifndef QT_NO_BEARERMANAGEMENT + netConfMan = new QNetworkConfigurationManager(this); + networkConfiguration = netConfMan->defaultConfiguration(); + networkSession.reset(new QNetworkSession(networkConfiguration)); + if (!networkSession->isOpen()) { + networkSession->open(); + QVERIFY(networkSession->waitForOpened(30000)); + } +#endif +} + +void tst_QNetworkReply::cleanupTestCase() +{ +#if !defined Q_OS_WIN + QFile::remove(wronlyFileName); +#endif + if (networkSession && networkSession->isOpen()) { + networkSession->close(); + } +} + +void tst_QNetworkReply::init() +{ + cleanup(); +} + +void tst_QNetworkReply::cleanup() +{ + QFile file(testFileName); + QVERIFY(!file.exists() || file.remove()); + + // clear the internal cache + manager.clearAccessCache(); + manager.setProxy(QNetworkProxy()); + manager.setCache(0); + + // clear cookies + cookieJar->setAllCookies(QList()); +} + +void tst_QNetworkReply::stateChecking() +{ + QUrl url = QUrl("file:///"); + QNetworkRequest req(url); // you can't open this file, I know + QNetworkReplyPtr reply = manager.get(req); + + QVERIFY(reply.data()); + QVERIFY(reply->isOpen()); + QVERIFY(reply->isReadable()); + QVERIFY(!reply->isWritable()); + + // both behaviours are OK since we might change underlying behaviour again + if (!reply->isFinished()) + QCOMPARE(reply->errorString(), QString("Unknown error")); + else + QVERIFY(!reply->errorString().isEmpty()); + + + QCOMPARE(reply->manager(), &manager); + QCOMPARE(reply->request(), req); + QCOMPARE(int(reply->operation()), int(QNetworkAccessManager::GetOperation)); + // error and not error are OK since we might change underlying behaviour again + if (!reply->isFinished()) + QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->url(), url); + + reply->abort(); +} + +void tst_QNetworkReply::invalidProtocol() +{ + QUrl url = QUrl::fromEncoded("not-a-known-protocol://foo/bar"); + QNetworkRequest req(url); + QNetworkReplyPtr reply; + + QString errorMsg = "Request failed: Protocol \"not-a-known-protocol\" is unknown"; + QString result = runSimpleRequest(QNetworkAccessManager::GetOperation, req, reply); + QCOMPARE(result, errorMsg); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::ProtocolUnknownError); +} + +void tst_QNetworkReply::getFromData_data() +{ + QTest::addColumn("request"); + QTest::addColumn("expected"); + QTest::addColumn("mimeType"); + + const QString defaultMimeType("text/plain;charset=US-ASCII"); + + //QTest::newRow("empty") << "data:" << QByteArray() << defaultMimeType; + QTest::newRow("empty2") << "data:," << QByteArray() << defaultMimeType; + QTest::newRow("just-charset_1") << "data:charset=iso-8859-1," + << QByteArray() << "text/plain;charset=iso-8859-1"; + QTest::newRow("just-charset_2") << "data:charset = iso-8859-1 ," + << QByteArray() << "text/plain;charset = iso-8859-1"; + //QTest::newRow("just-media") << "data:text/xml" << QByteArray() << "text/xml"; + QTest::newRow("just-media2") << "data:text/xml," << QByteArray() << "text/xml"; + + QTest::newRow("plain_1") << "data:,foo" << QByteArray("foo") << defaultMimeType; + QTest::newRow("plain_2") << "data:text/html,Hello World" << QByteArray("Hello World") + << "text/html"; + QTest::newRow("plain_3") << "data:text/html;charset=utf-8,Hello World" + << QByteArray("Hello World") << "text/html;charset=utf-8"; + + QTest::newRow("pct_1") << "data:,%3Cbody%20contentEditable%3Dtrue%3E%0D%0A" + << QByteArray("\r\n") << defaultMimeType; + QTest::newRow("pct_2") << "data:text/html;charset=utf-8,%3Cbody%20contentEditable%3Dtrue%3E%0D%0A" + << QByteArray("\r\n") + << "text/html;charset=utf-8"; + + QTest::newRow("base64-empty_1") << "data:;base64," << QByteArray() << defaultMimeType; + QTest::newRow("base64-empty_2") << "data:charset=utf-8;base64," << QByteArray() + << "text/plain;charset=utf-8"; + QTest::newRow("base64-empty_3") << "data:text/html;charset=utf-8;base64," + << QByteArray() << "text/html;charset=utf-8"; + + QTest::newRow("base64_1") << "data:;base64,UXQgaXMgZ3JlYXQh" << QByteArray("Qt is great!") + << defaultMimeType; + QTest::newRow("base64_2") << "data:charset=utf-8;base64,UXQgaXMgZ3JlYXQh" + << QByteArray("Qt is great!") << "text/plain;charset=utf-8"; + QTest::newRow("base64_3") << "data:text/html;charset=utf-8;base64,UXQgaXMgZ3JlYXQh" + << QByteArray("Qt is great!") << "text/html;charset=utf-8"; + + QTest::newRow("pct-nul") << "data:,a%00g" << QByteArray("a\0g", 3) << defaultMimeType; + QTest::newRow("base64-nul") << "data:;base64,YQBn" << QByteArray("a\0g", 3) << defaultMimeType; + QTest::newRow("pct-nonutf8") << "data:,a%E1g" << QByteArray("a\xE1g", 3) << defaultMimeType; + + QTest::newRow("base64") + << QString::fromLatin1("data:application/xml;base64,PGUvPg==") + << QByteArray("") + << "application/xml"; + + QTest::newRow("base64, no media type") + << QString::fromLatin1("data:;base64,PGUvPg==") + << QByteArray("") + << defaultMimeType; + + QTest::newRow("Percent encoding") + << QString::fromLatin1("data:application/xml,%3Ce%2F%3E") + << QByteArray("") + << "application/xml"; + + QTest::newRow("Percent encoding, no media type") + << QString::fromLatin1("data:,%3Ce%2F%3E") + << QByteArray("") + << defaultMimeType; + + QTest::newRow("querychars") + << QString::fromLatin1("data:,foo?x=0&y=0") + << QByteArray("foo?x=0&y=0") + << defaultMimeType; + + QTest::newRow("css") << "data:text/css,div%20{%20border-right:%20solid;%20}" + << QByteArray("div { border-right: solid; }") + << "text/css"; +} + +void tst_QNetworkReply::getFromData() +{ + QFETCH(QString, request); + QFETCH(QByteArray, expected); + QFETCH(QString, mimeType); + + QUrl url = QUrl::fromEncoded(request.toLatin1()); + QNetworkRequest req(url); + QNetworkReplyPtr reply; + + RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, req, reply)); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::NoError); + + QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader).toString(), mimeType); + QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), qint64(expected.size())); + QCOMPARE(reply->readAll(), expected); +} + +void tst_QNetworkReply::getFromFile() +{ + // create the file: + QTemporaryFile file(QDir::currentPath() + "/temp-XXXXXX"); + file.setAutoRemove(true); + QVERIFY(file.open()); + + QNetworkRequest request(QUrl::fromLocalFile(file.fileName())); + QNetworkReplyPtr reply; + + static const char fileData[] = "This is some data that is in the file.\r\n"; + QByteArray data = QByteArray::fromRawData(fileData, sizeof fileData - 1); + QVERIFY(file.write(data) == data.size()); + file.flush(); + QCOMPARE(file.size(), qint64(data.size())); + + RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply)); + + QCOMPARE(reply->url(), request.url()); + QCOMPARE(reply->error(), QNetworkReply::NoError); + + QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), file.size()); + QCOMPARE(reply->readAll(), data); + + // make the file bigger + file.resize(0); + const int multiply = (128 * 1024) / (sizeof fileData - 1); + for (int i = 0; i < multiply; ++i) + file.write(fileData, sizeof fileData - 1); + file.flush(); + + // run again + reply = 0; + + RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply)); + QCOMPARE(reply->url(), request.url()); + QCOMPARE(reply->error(), QNetworkReply::NoError); + + QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), file.size()); + QCOMPARE(qint64(reply->readAll().size()), file.size()); +} + +void tst_QNetworkReply::getFromFileSpecial_data() +{ + QTest::addColumn("fileName"); + QTest::addColumn("url"); + + QTest::newRow("resource") << ":/resource" << "qrc:/resource"; + QTest::newRow("search-path") << "srcdir:/rfc3252.txt" << "srcdir:/rfc3252.txt"; + QTest::newRow("bigfile-path") << "srcdir:/bigfile" << "srcdir:/bigfile"; +#ifdef Q_OS_WIN + QTest::newRow("smb-path") << "srcdir:/smb-file.txt" << "file://" + QtNetworkSettings::winServerName() + "/testshare/test.pri"; +#endif +} + +void tst_QNetworkReply::getFromFileSpecial() +{ + QFETCH(QString, fileName); + QFETCH(QString, url); + + // open the resource so we can find out its size + QFile resource(fileName); + QVERIFY(resource.open(QIODevice::ReadOnly)); + + QNetworkRequest request; + QNetworkReplyPtr reply; + request.setUrl(url); + RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply)); + + QCOMPARE(reply->url(), request.url()); + QCOMPARE(reply->error(), QNetworkReply::NoError); + + QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), resource.size()); + QCOMPARE(reply->readAll(), resource.readAll()); +} + +void tst_QNetworkReply::getFromFtp_data() +{ + QTest::addColumn("referenceName"); + QTest::addColumn("url"); + + QTest::newRow("rfc3252.txt") << SRCDIR "/rfc3252.txt" << "ftp://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"; + QTest::newRow("bigfile") << SRCDIR "/bigfile" << "ftp://" + QtNetworkSettings::serverName() + "/qtest/bigfile"; +} + +void tst_QNetworkReply::getFromFtp() +{ + QFETCH(QString, referenceName); + QFETCH(QString, url); + + QFile reference(referenceName); + QVERIFY(reference.open(QIODevice::ReadOnly)); + + QNetworkRequest request(url); + QNetworkReplyPtr reply; + RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply)); + + QCOMPARE(reply->url(), request.url()); + QCOMPARE(reply->error(), QNetworkReply::NoError); + + QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size()); + QCOMPARE(reply->readAll(), reference.readAll()); +} + +void tst_QNetworkReply::getFromHttp_data() +{ + QTest::addColumn("referenceName"); + QTest::addColumn("url"); + + QTest::newRow("success-internal") << SRCDIR "/rfc3252.txt" << "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"; + QTest::newRow("success-external") << SRCDIR "/rfc3252.txt" << "http://www.ietf.org/rfc/rfc3252.txt"; + QTest::newRow("bigfile-internal") << SRCDIR "/bigfile" << "http://" + QtNetworkSettings::serverName() + "/qtest/bigfile"; +} + +void tst_QNetworkReply::getFromHttp() +{ + QFETCH(QString, referenceName); + QFETCH(QString, url); + + QFile reference(referenceName); + QVERIFY(reference.open(QIODevice::ReadOnly)); + + QNetworkRequest request(url); + QNetworkReplyPtr reply; + RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply)); + + QCOMPARE(reply->url(), request.url()); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + QCOMPARE(reply->size(), reference.size()); + // only compare when the header is set. + if (reply->header(QNetworkRequest::ContentLengthHeader).isValid()) + QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size()); + + // We know our internal server is apache.. + if (qstrcmp(QTest::currentDataTag(), "success-internal") == 0) + QVERIFY(reply->header(QNetworkRequest::ServerHeader).toString().contains("Apache")); + + QCOMPARE(reply->readAll(), reference.readAll()); +} + +void tst_QNetworkReply::getErrors_data() +{ + QTest::addColumn("url"); + QTest::addColumn("error"); + QTest::addColumn("httpStatusCode"); + QTest::addColumn("dataIsEmpty"); + + // empties + QTest::newRow("empty-url") << QString() << int(QNetworkReply::ProtocolUnknownError) << 0 << true; + QTest::newRow("empty-scheme-host") << SRCDIR "/rfc3252.txt" << int(QNetworkReply::ProtocolUnknownError) << 0 << true; + QTest::newRow("empty-scheme") << "//" + QtNetworkSettings::winServerName() + "/testshare/test.pri" + << int(QNetworkReply::ProtocolUnknownError) << 0 << true; + + // file: errors + QTest::newRow("file-host") << "file://this-host-doesnt-exist.troll.no/foo.txt" +#if !defined Q_OS_WIN + << int(QNetworkReply::ProtocolInvalidOperationError) << 0 << true; +#else + << int(QNetworkReply::ContentNotFoundError) << 0 << true; +#endif + QTest::newRow("file-no-path") << "file://localhost" + << int(QNetworkReply::ContentOperationNotPermittedError) << 0 << true; + QTest::newRow("file-is-dir") << QUrl::fromLocalFile(QDir::currentPath()).toString() + << int(QNetworkReply::ContentOperationNotPermittedError) << 0 << true; + QTest::newRow("file-exist") << QUrl::fromLocalFile(QDir::currentPath() + "/this-file-doesnt-exist.txt").toString() + << int(QNetworkReply::ContentNotFoundError) << 0 << true; +#if !defined Q_OS_WIN && !defined(Q_OS_SYMBIAN) + QTest::newRow("file-is-wronly") << QUrl::fromLocalFile(wronlyFileName).toString() + << int(QNetworkReply::ContentAccessDenied) << 0 << true; +#endif + if (QFile::exists("/etc/shadow")) + QTest::newRow("file-permissions") << "file:/etc/shadow" + << int(QNetworkReply::ContentAccessDenied) << 0 << true; + + // ftp: errors + QTest::newRow("ftp-host") << "ftp://this-host-doesnt-exist.troll.no/foo.txt" + << int(QNetworkReply::HostNotFoundError) << 0 << true; + QTest::newRow("ftp-no-path") << "ftp://" + QtNetworkSettings::serverName() + << int(QNetworkReply::ContentOperationNotPermittedError) << 0 << true; + QTest::newRow("ftp-is-dir") << "ftp://" + QtNetworkSettings::serverName() + "/qtest" + << int(QNetworkReply::ContentOperationNotPermittedError) << 0 << true; + QTest::newRow("ftp-dir-not-readable") << "ftp://" + QtNetworkSettings::serverName() + "/pub/dir-not-readable/foo.txt" + << int(QNetworkReply::ContentAccessDenied) << 0 << true; + QTest::newRow("ftp-file-not-readable") << "ftp://" + QtNetworkSettings::serverName() + "/pub/file-not-readable.txt" + << int(QNetworkReply::ContentAccessDenied) << 0 << true; + QTest::newRow("ftp-exist") << "ftp://" + QtNetworkSettings::serverName() + "/pub/this-file-doesnt-exist.txt" + << int(QNetworkReply::ContentNotFoundError) << 0 << true; + + // http: errors + QTest::newRow("http-host") << "http://this-host-will-never-exist.troll.no/" + << int(QNetworkReply::HostNotFoundError) << 0 << true; + QTest::newRow("http-exist") << "http://" + QtNetworkSettings::serverName() + "/this-file-doesnt-exist.txt" + << int(QNetworkReply::ContentNotFoundError) << 404 << false; + QTest::newRow("http-authentication") << "http://" + QtNetworkSettings::serverName() + "/qtest/rfcs-auth" + << int(QNetworkReply::AuthenticationRequiredError) << 401 << false; +} + +void tst_QNetworkReply::getErrors() +{ + QFETCH(QString, url); + QNetworkRequest request(url); + + QNetworkReplyPtr reply = manager.get(request); + reply->setParent(this); // we have expect-fails + + if (!reply->isFinished()) + QCOMPARE(reply->error(), QNetworkReply::NoError); + + // now run the request: + connect(reply, SIGNAL(finished()), + &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + //qDebug() << reply->errorString(); + + QFETCH(int, error); + QEXPECT_FAIL("ftp-is-dir", "QFtp cannot provide enough detail", Abort); + // the line below is not necessary + QEXPECT_FAIL("ftp-dir-not-readable", "QFtp cannot provide enough detail", Abort); + QCOMPARE(reply->error(), QNetworkReply::NetworkError(error)); + + QTEST(reply->readAll().isEmpty(), "dataIsEmpty"); + + QVERIFY(reply->isFinished()); + QVERIFY(!reply->isRunning()); + + QFETCH(int, httpStatusCode); + if (httpStatusCode != 0) { + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), httpStatusCode); + } +} + +static inline QByteArray md5sum(const QByteArray &data) +{ + return QCryptographicHash::hash(data, QCryptographicHash::Md5); +} + +void tst_QNetworkReply::putToFile_data() +{ + QTest::addColumn("data"); + QTest::addColumn("md5sum"); + + QByteArray data; + data = ""; + QTest::newRow("empty") << data << md5sum(data); + + data = "This is a normal message."; + QTest::newRow("generic") << data << md5sum(data); + + data = "This is a message to show that Qt rocks!\r\n\n"; + QTest::newRow("small") << data << md5sum(data); + + data = QByteArray("abcd\0\1\2\abcd",12); + QTest::newRow("with-nul") << data << md5sum(data); + + data = QByteArray(4097, '\4'); + QTest::newRow("4k+1") << data << md5sum(data); + + data = QByteArray(128*1024+1, '\177'); + QTest::newRow("128k+1") << data << md5sum(data); + + data = QByteArray(2*1024*1024+1, '\177'); + QTest::newRow("2MB+1") << data << md5sum(data); +} + +void tst_QNetworkReply::putToFile() +{ + QFile file(testFileName); + + QUrl url = QUrl::fromLocalFile(file.fileName()); + QNetworkRequest request(url); + QNetworkReplyPtr reply; + + QFETCH(QByteArray, data); + + RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PutOperation, request, reply, data)); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0)); + QVERIFY(reply->readAll().isEmpty()); + + QVERIFY(file.open(QIODevice::ReadOnly)); + QCOMPARE(file.size(), qint64(data.size())); + QByteArray contents = file.readAll(); + QCOMPARE(contents, data); +} + +void tst_QNetworkReply::putToFtp_data() +{ + putToFile_data(); +} + +void tst_QNetworkReply::putToFtp() +{ + QUrl url("ftp://" + QtNetworkSettings::serverName()); + url.setPath(QString("/qtest/upload/qnetworkaccess-putToFtp-%1-%2") + .arg(QTest::currentDataTag()) + .arg(uniqueExtension)); + + QNetworkRequest request(url); + QNetworkReplyPtr reply; + + QFETCH(QByteArray, data); + + RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PutOperation, request, reply, data)); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0)); + QVERIFY(reply->readAll().isEmpty()); + + // download the file again from FTP to make sure it was uploaded + // correctly + QFtp ftp; + ftp.connectToHost(url.host()); + ftp.login(); + ftp.get(url.path()); + + QObject::connect(&ftp, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QObject::disconnect(&ftp, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop())); + + QByteArray uploaded = ftp.readAll(); + QCOMPARE(uploaded.size(), data.size()); + QCOMPARE(uploaded, data); + + ftp.close(); + QObject::connect(&ftp, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QObject::disconnect(&ftp, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop())); +} + +void tst_QNetworkReply::putToHttp_data() +{ + putToFile_data(); +} + +void tst_QNetworkReply::putToHttp() +{ + QUrl url("http://" + QtNetworkSettings::serverName()); + url.setPath(QString("/dav/qnetworkaccess-putToHttp-%1-%2") + .arg(QTest::currentDataTag()) + .arg(uniqueExtension)); + + QNetworkRequest request(url); + QNetworkReplyPtr reply; + + QFETCH(QByteArray, data); + + RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PutOperation, request, reply, data)); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::NoError); + + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 201); // 201 Created + + // download the file again from HTTP to make sure it was uploaded + // correctly. HTTP/0.9 is enough + QTcpSocket socket; + socket.connectToHost(QtNetworkSettings::serverName(), 80); + socket.write("GET " + url.toEncoded(QUrl::RemoveScheme | QUrl::RemoveAuthority) + "\r\n"); + if (!socket.waitForDisconnected(10000)) + QFAIL("Network timeout"); + + QByteArray uploadedData = socket.readAll(); + QCOMPARE(uploadedData, data); +} + +void tst_QNetworkReply::putToHttpSynchronous_data() +{ + uniqueExtension = createUniqueExtension(); + putToFile_data(); +} + +void tst_QNetworkReply::putToHttpSynchronous() +{ + QUrl url("http://" + QtNetworkSettings::serverName()); + url.setPath(QString("/dav/qnetworkaccess-putToHttp-%1-%2") + .arg(QTest::currentDataTag()) + .arg(uniqueExtension)); + + QNetworkRequest request(url); + QNetworkReplyPtr reply; + + QFETCH(QByteArray, data); + + request.setAttribute( + QNetworkRequest::SynchronousRequestAttribute, + true); + + RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PutOperation, request, reply, data)); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::NoError); + + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 201); // 201 Created + + // download the file again from HTTP to make sure it was uploaded + // correctly. HTTP/0.9 is enough + QTcpSocket socket; + socket.connectToHost(QtNetworkSettings::serverName(), 80); + socket.write("GET " + url.toEncoded(QUrl::RemoveScheme | QUrl::RemoveAuthority) + "\r\n"); + if (!socket.waitForDisconnected(10000)) + QFAIL("Network timeout"); + + QByteArray uploadedData = socket.readAll(); + QCOMPARE(uploadedData, data); +} + +void tst_QNetworkReply::postToHttp_data() +{ + putToFile_data(); +} + +void tst_QNetworkReply::postToHttp() +{ + QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/md5sum.cgi"); + + QNetworkRequest request(url); + request.setRawHeader("Content-Type", "application/octet-stream"); + QNetworkReplyPtr reply; + + QFETCH(QByteArray, data); + + RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PostOperation, request, reply, data)); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::NoError); + + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok + + QFETCH(QByteArray, md5sum); + QByteArray uploadedData = reply->readAll().trimmed(); + QCOMPARE(uploadedData, md5sum.toHex()); +} + +void tst_QNetworkReply::postToHttpSynchronous_data() +{ + putToFile_data(); +} + +void tst_QNetworkReply::postToHttpSynchronous() +{ + QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/md5sum.cgi"); + + QNetworkRequest request(url); + request.setRawHeader("Content-Type", "application/octet-stream"); + + request.setAttribute( + QNetworkRequest::SynchronousRequestAttribute, + true); + + QNetworkReplyPtr reply; + + QFETCH(QByteArray, data); + + RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PostOperation, request, reply, data)); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::NoError); + + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok + + QFETCH(QByteArray, md5sum); + QByteArray uploadedData = reply->readAll().trimmed(); + QCOMPARE(uploadedData, md5sum.toHex()); +} + +void tst_QNetworkReply::postToHttpMultipart_data() +{ + QTest::addColumn("url"); + QTest::addColumn("multiPart"); + QTest::addColumn("expectedReplyData"); + QTest::addColumn("contentType"); + + QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/multipart.cgi"); + QByteArray expectedData; + + + // empty parts + + QHttpMultiPart *emptyMultiPart = new QHttpMultiPart; + QTest::newRow("empty") << url << emptyMultiPart << expectedData << QByteArray("mixed"); + + QHttpMultiPart *emptyRelatedMultiPart = new QHttpMultiPart; + emptyRelatedMultiPart->setContentType(QHttpMultiPart::RelatedType); + QTest::newRow("empty-related") << url << emptyRelatedMultiPart << expectedData << QByteArray("related"); + + QHttpMultiPart *emptyAlternativeMultiPart = new QHttpMultiPart; + emptyAlternativeMultiPart->setContentType(QHttpMultiPart::AlternativeType); + QTest::newRow("empty-alternative") << url << emptyAlternativeMultiPart << expectedData << QByteArray("alternative"); + + + // text-only parts + + QHttpPart textPart; + textPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain")); + textPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"text\"")); + textPart.setBody("7 bytes"); + QHttpMultiPart *multiPart1 = new QHttpMultiPart; + multiPart1->setContentType(QHttpMultiPart::FormDataType); + multiPart1->append(textPart); + expectedData = "key: text, value: 7 bytes\n"; + QTest::newRow("text") << url << multiPart1 << expectedData << QByteArray("form-data"); + + QHttpMultiPart *customMultiPart = new QHttpMultiPart; + customMultiPart->append(textPart); + expectedData = "header: Content-Type, value: 'text/plain'\n" + "header: Content-Disposition, value: 'form-data; name=\"text\"'\n" + "content: 7 bytes\n" + "\n"; + QTest::newRow("text-custom") << url << customMultiPart << expectedData << QByteArray("custom"); + + QHttpPart textPart2; + textPart2.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain")); + textPart2.setRawHeader("myRawHeader", "myValue"); + textPart2.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"text2\"")); + textPart2.setBody("some more bytes"); + textPart2.setBodyDevice((QIODevice *) 1); // test whether setting and unsetting of the device works + textPart2.setBodyDevice(0); + QHttpMultiPart *multiPart2 = new QHttpMultiPart; + multiPart2->setContentType(QHttpMultiPart::FormDataType); + multiPart2->append(textPart); + multiPart2->append(textPart2); + expectedData = "key: text2, value: some more bytes\n" + "key: text, value: 7 bytes\n"; + QTest::newRow("text-text") << url << multiPart2 << expectedData << QByteArray("form-data"); + + + QHttpPart textPart3; + textPart3.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain")); + textPart3.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"text3\"")); + textPart3.setRawHeader("Content-Location", "http://my.test.location.tld"); + textPart3.setBody("even more bytes"); + QHttpMultiPart *multiPart3 = new QHttpMultiPart; + multiPart3->setContentType(QHttpMultiPart::AlternativeType); + multiPart3->append(textPart); + multiPart3->append(textPart2); + multiPart3->append(textPart3); + expectedData = "header: Content-Type, value: 'text/plain'\n" + "header: Content-Disposition, value: 'form-data; name=\"text\"'\n" + "content: 7 bytes\n" + "\n" + "header: Content-Type, value: 'text/plain'\n" + "header: myRawHeader, value: 'myValue'\n" + "header: Content-Disposition, value: 'form-data; name=\"text2\"'\n" + "content: some more bytes\n" + "\n" + "header: Content-Type, value: 'text/plain'\n" + "header: Content-Disposition, value: 'form-data; name=\"text3\"'\n" + "header: Content-Location, value: 'http://my.test.location.tld'\n" + "content: even more bytes\n\n"; + QTest::newRow("text-text-text") << url << multiPart3 << expectedData << QByteArray("alternative"); + + + + // text and image parts + + QHttpPart imagePart11; + imagePart11.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg")); + imagePart11.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"testImage\"")); + imagePart11.setRawHeader("Content-Location", "http://my.test.location.tld"); + imagePart11.setRawHeader("Content-ID", "my@id.tld"); + QFile *file11 = new QFile(SRCDIR "/image1.jpg"); + file11->open(QIODevice::ReadOnly); + imagePart11.setBodyDevice(file11); + QHttpMultiPart *imageMultiPart1 = new QHttpMultiPart(QHttpMultiPart::FormDataType); + imageMultiPart1->append(imagePart11); + file11->setParent(imageMultiPart1); + expectedData = "key: testImage, value: 87ef3bb319b004ba9e5e9c9fa713776e\n"; // md5 sum of file + QTest::newRow("image") << url << imageMultiPart1 << expectedData << QByteArray("form-data"); + + QHttpPart imagePart21; + imagePart21.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg")); + imagePart21.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"testImage1\"")); + imagePart21.setRawHeader("Content-Location", "http://my.test.location.tld"); + imagePart21.setRawHeader("Content-ID", "my@id.tld"); + QFile *file21 = new QFile(SRCDIR "/image1.jpg"); + file21->open(QIODevice::ReadOnly); + imagePart21.setBodyDevice(file21); + QHttpMultiPart *imageMultiPart2 = new QHttpMultiPart(); + imageMultiPart2->setContentType(QHttpMultiPart::FormDataType); + imageMultiPart2->append(textPart); + imageMultiPart2->append(imagePart21); + file21->setParent(imageMultiPart2); + QHttpPart imagePart22; + imagePart22.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg")); + imagePart22.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"testImage2\"")); + QFile *file22 = new QFile(SRCDIR "/image2.jpg"); + file22->open(QIODevice::ReadOnly); + imagePart22.setBodyDevice(file22); + imageMultiPart2->append(imagePart22); + file22->setParent(imageMultiPart2); + expectedData = "key: testImage1, value: 87ef3bb319b004ba9e5e9c9fa713776e\n" + "key: text, value: 7 bytes\n" + "key: testImage2, value: 483761b893f7fb1bd2414344cd1f3dfb\n"; + QTest::newRow("text-image-image") << url << imageMultiPart2 << expectedData << QByteArray("form-data"); + + + QHttpPart imagePart31; + imagePart31.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg")); + imagePart31.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"testImage1\"")); + imagePart31.setRawHeader("Content-Location", "http://my.test.location.tld"); + imagePart31.setRawHeader("Content-ID", "my@id.tld"); + QFile *file31 = new QFile(SRCDIR "/image1.jpg"); + file31->open(QIODevice::ReadOnly); + imagePart31.setBodyDevice(file31); + QHttpMultiPart *imageMultiPart3 = new QHttpMultiPart(QHttpMultiPart::FormDataType); + imageMultiPart3->append(imagePart31); + file31->setParent(imageMultiPart3); + QHttpPart imagePart32; + imagePart32.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg")); + imagePart32.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"testImage2\"")); + QFile *file32 = new QFile(SRCDIR "/image2.jpg"); + file32->open(QIODevice::ReadOnly); + imagePart32.setBodyDevice(file31); // check that resetting works + imagePart32.setBodyDevice(file32); + imageMultiPart3->append(imagePart32); + file32->setParent(imageMultiPart3); + QHttpPart imagePart33; + imagePart33.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg")); + imagePart33.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"testImage3\"")); + QFile *file33 = new QFile(SRCDIR "/image3.jpg"); + file33->open(QIODevice::ReadOnly); + imagePart33.setBodyDevice(file33); + imageMultiPart3->append(imagePart33); + file33->setParent(imageMultiPart3); + expectedData = "key: testImage1, value: 87ef3bb319b004ba9e5e9c9fa713776e\n" + "key: testImage2, value: 483761b893f7fb1bd2414344cd1f3dfb\n" + "key: testImage3, value: ab0eb6fd4fcf8b4436254870b4513033\n"; + QTest::newRow("3-images") << url << imageMultiPart3 << expectedData << QByteArray("form-data"); + + + // note: nesting multiparts is not working currently; for that, the outputDevice would need to be public + +// QHttpPart imagePart41; +// imagePart41.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg")); +// QFile *file41 = new QFile(SRCDIR "/image1.jpg"); +// file41->open(QIODevice::ReadOnly); +// imagePart41.setBodyDevice(file41); +// +// QHttpMultiPart *innerMultiPart = new QHttpMultiPart(); +// innerMultiPart->setContentType(QHttpMultiPart::FormDataType); +// textPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant()); +// innerMultiPart->append(textPart); +// innerMultiPart->append(imagePart41); +// textPart2.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant()); +// innerMultiPart->append(textPart2); +// +// QHttpPart nestedPart; +// nestedPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"nestedMessage")); +// nestedPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("multipart/alternative; boundary=\"" + innerMultiPart->boundary() + "\"")); +// innerMultiPart->outputDevice()->open(QIODevice::ReadOnly); +// nestedPart.setBodyDevice(innerMultiPart->outputDevice()); +// +// QHttpMultiPart *outerMultiPart = new QHttpMultiPart; +// outerMultiPart->setContentType(QHttpMultiPart::FormDataType); +// outerMultiPart->append(textPart); +// outerMultiPart->append(nestedPart); +// outerMultiPart->append(textPart2); +// expectedData = "nothing"; // the CGI.pm module running on the test server does not understand nested multiparts +// openFiles.clear(); +// openFiles << file41; +// QTest::newRow("nested") << url << outerMultiPart << expectedData << openFiles; + + + // test setting large chunks of content with a byte array instead of a device (DISCOURAGED because of high memory consumption, + // but we need to test that the behavior is correct) + QHttpPart imagePart51; + imagePart51.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg")); + imagePart51.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"testImage\"")); + QFile *file51 = new QFile(SRCDIR "/image1.jpg"); + file51->open(QIODevice::ReadOnly); + QByteArray imageData = file51->readAll(); + file51->close(); + delete file51; + imagePart51.setBody("7 bytes"); // check that resetting works + imagePart51.setBody(imageData); + QHttpMultiPart *imageMultiPart5 = new QHttpMultiPart; + imageMultiPart5->setContentType(QHttpMultiPart::FormDataType); + imageMultiPart5->append(imagePart51); + expectedData = "key: testImage, value: 87ef3bb319b004ba9e5e9c9fa713776e\n"; // md5 sum of file + QTest::newRow("image-as-content") << url << imageMultiPart5 << expectedData << QByteArray("form-data"); +} + +void tst_QNetworkReply::postToHttpMultipart() +{ + QFETCH(QUrl, url); + + static QSet boundaries; + + QNetworkRequest request(url); + QNetworkReplyPtr reply; + + QFETCH(QHttpMultiPart *, multiPart); + QFETCH(QByteArray, expectedReplyData); + QFETCH(QByteArray, contentType); + + // hack for testing the setting of the content-type header by hand: + if (contentType == "custom") { + QByteArray contentType("multipart/custom; boundary=\"" + multiPart->boundary() + "\""); + request.setHeader(QNetworkRequest::ContentTypeHeader, contentType); + } + + QVERIFY2(! boundaries.contains(multiPart->boundary()), "boundary '" + multiPart->boundary() + "' has been created twice"); + boundaries.insert(multiPart->boundary()); + + RUN_REQUEST(runMultipartRequest(request, reply, multiPart, "POST")); + multiPart->deleteLater(); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::NoError); + + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok + + QVERIFY(multiPart->boundary().count() > 20); // check that there is randomness after the "boundary_.oOo._" string + QVERIFY(multiPart->boundary().count() < 70); + QByteArray replyData = reply->readAll(); + + expectedReplyData.prepend("content type: multipart/" + contentType + "; boundary=\"" + multiPart->boundary() + "\"\n"); +// QEXPECT_FAIL("nested", "the server does not understand nested multipart messages", Continue); // see above + QCOMPARE(replyData, expectedReplyData); +} + +void tst_QNetworkReply::putToHttpMultipart_data() +{ + postToHttpMultipart_data(); +} + +void tst_QNetworkReply::putToHttpMultipart() +{ + QSKIP("test server script cannot handle PUT data yet", SkipAll); + QFETCH(QUrl, url); + + static QSet boundaries; + + QNetworkRequest request(url); + QNetworkReplyPtr reply; + + QFETCH(QHttpMultiPart *, multiPart); + QFETCH(QByteArray, expectedReplyData); + QFETCH(QByteArray, contentType); + + // hack for testing the setting of the content-type header by hand: + if (contentType == "custom") { + QByteArray contentType("multipart/custom; boundary=\"" + multiPart->boundary() + "\""); + request.setHeader(QNetworkRequest::ContentTypeHeader, contentType); + } + + QVERIFY2(! boundaries.contains(multiPart->boundary()), "boundary '" + multiPart->boundary() + "' has been created twice"); + boundaries.insert(multiPart->boundary()); + + RUN_REQUEST(runMultipartRequest(request, reply, multiPart, "PUT")); + multiPart->deleteLater(); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::NoError); + + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok + + QVERIFY(multiPart->boundary().count() > 20); // check that there is randomness after the "boundary_.oOo._" string + QVERIFY(multiPart->boundary().count() < 70); + QByteArray replyData = reply->readAll(); + + expectedReplyData.prepend("content type: multipart/" + contentType + "; boundary=\"" + multiPart->boundary() + "\"\n"); +// QEXPECT_FAIL("nested", "the server does not understand nested multipart messages", Continue); // see above + QCOMPARE(replyData, expectedReplyData); +} + +void tst_QNetworkReply::deleteFromHttp_data() +{ + QTest::addColumn("url"); + QTest::addColumn("resultCode"); + QTest::addColumn("error"); + + // for status codes to expect, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html + + QTest::newRow("405-method-not-allowed") << QUrl("http://" + QtNetworkSettings::serverName() + "/index.html") << 405 << QNetworkReply::ContentOperationNotPermittedError; + QTest::newRow("200-ok") << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/http-delete.cgi?200-ok") << 200 << QNetworkReply::NoError; + QTest::newRow("202-accepted") << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/http-delete.cgi?202-accepted") << 202 << QNetworkReply::NoError; + QTest::newRow("204-no-content") << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/http-delete.cgi?204-no-content") << 204 << QNetworkReply::NoError; + QTest::newRow("404-not-found") << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/http-delete.cgi?404-not-found") << 404 << QNetworkReply::ContentNotFoundError; +} + +void tst_QNetworkReply::deleteFromHttp() +{ + QFETCH(QUrl, url); + QFETCH(int, resultCode); + QFETCH(QNetworkReply::NetworkError, error); + QNetworkRequest request(url); + QNetworkReplyPtr reply; + runSimpleRequest(QNetworkAccessManager::DeleteOperation, request, reply, 0); + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), error); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), resultCode); +} + +void tst_QNetworkReply::putGetDeleteGetFromHttp_data() +{ + QTest::addColumn("putUrl"); + QTest::addColumn("putResultCode"); + QTest::addColumn("putError"); + QTest::addColumn("deleteUrl"); + QTest::addColumn("deleteResultCode"); + QTest::addColumn("deleteError"); + QTest::addColumn("get2Url"); + QTest::addColumn("get2ResultCode"); + QTest::addColumn("get2Error"); + + QUrl url("http://" + QtNetworkSettings::serverName()); + url.setPath(QString("/dav/qnetworkaccess-putToHttp-%1-%2") + .arg(QTest::currentDataTag()) + .arg(uniqueExtension)); + + // first use case: put, get (to check it is there), delete, get (to check it is not there anymore) + QTest::newRow("success") << url << 201 << QNetworkReply::NoError << url << 204 << QNetworkReply::NoError << url << 404 << QNetworkReply::ContentNotFoundError; + + QUrl wrongUrl("http://" + QtNetworkSettings::serverName()); + wrongUrl.setPath(QString("/dav/qnetworkaccess-thisURLisNotAvailable")); + + // second use case: put, get (to check it is there), delete wrong URL, get (to check it is still there) + QTest::newRow("delete-error") << url << 201 << QNetworkReply::NoError << wrongUrl << 404 << QNetworkReply::ContentNotFoundError << url << 200 << QNetworkReply::NoError; + +} + +void tst_QNetworkReply::putGetDeleteGetFromHttp() +{ + QFETCH(QUrl, putUrl); + QFETCH(int, putResultCode); + QFETCH(QNetworkReply::NetworkError, putError); + QFETCH(QUrl, deleteUrl); + QFETCH(int, deleteResultCode); + QFETCH(QNetworkReply::NetworkError, deleteError); + QFETCH(QUrl, get2Url); + QFETCH(int, get2ResultCode); + QFETCH(QNetworkReply::NetworkError, get2Error); + + QNetworkRequest putRequest(putUrl); + QNetworkRequest deleteRequest(deleteUrl); + QNetworkRequest get2Request(get2Url); + QNetworkReplyPtr reply; + + RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PutOperation, putRequest, reply, 0)); + QCOMPARE(reply->error(), putError); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), putResultCode); + + runSimpleRequest(QNetworkAccessManager::GetOperation, putRequest, reply, 0); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + + runSimpleRequest(QNetworkAccessManager::DeleteOperation, deleteRequest, reply, 0); + QCOMPARE(reply->error(), deleteError); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), deleteResultCode); + + runSimpleRequest(QNetworkAccessManager::GetOperation, get2Request, reply, 0); + QCOMPARE(reply->error(), get2Error); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), get2ResultCode); + +} + +void tst_QNetworkReply::connectToIPv6Address_data() +{ + QTest::addColumn("url"); + QTest::addColumn("error"); + QTest::addColumn("dataToSend"); + QTest::addColumn("hostfield"); + QTest::newRow("localhost") << QUrl(QByteArray("http://[::1]")) << QNetworkReply::NoError<< QByteArray("localhost") << QByteArray("[::1]"); + //QTest::newRow("ipv4localhost") << QUrl(QByteArray("http://127.0.0.1")) << QNetworkReply::NoError<< QByteArray("ipv4localhost") << QByteArray("127.0.0.1"); + //to add more test data here +} + +void tst_QNetworkReply::connectToIPv6Address() +{ + QFETCH(QUrl, url); + QFETCH(QNetworkReply::NetworkError, error); + QFETCH(QByteArray, dataToSend); + QFETCH(QByteArray, hostfield); + + QByteArray httpResponse = QByteArray("HTTP/1.0 200 OK\r\nContent-Length: "); + httpResponse += QByteArray::number(dataToSend.size()); + httpResponse += "\r\n\r\n"; + httpResponse += dataToSend; + + MiniHttpServer server(httpResponse, false, NULL/*thread*/, true/*useipv6*/); + server.doClose = true; + + url.setPort(server.serverPort()); + QNetworkRequest request(url); + + QNetworkReplyPtr reply = manager.get(request); + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + QByteArray content = reply->readAll(); + //qDebug() << server.receivedData; + QByteArray hostinfo = "\r\nHost: " + hostfield + ":" + QByteArray::number(server.serverPort()) + "\r\n"; + QVERIFY(server.receivedData.contains(hostinfo)); + QVERIFY(content == dataToSend); + QCOMPARE(reply->url(), request.url()); + QVERIFY(reply->error() == error); +} + +void tst_QNetworkReply::sendCustomRequestToHttp_data() +{ + QTest::addColumn("url"); + QTest::addColumn("verb"); + QTest::addColumn("device"); + QTest::addColumn("resultCode"); + QTest::addColumn("error"); + QTest::addColumn("expectedContent"); + + QTest::newRow("options") << QUrl("http://" + QtNetworkSettings::serverName()) << + QByteArray("OPTIONS") << (QBuffer *) 0 << 200 << QNetworkReply::NoError << QByteArray(); + QTest::newRow("trace") << QUrl("http://" + QtNetworkSettings::serverName()) << + QByteArray("TRACE") << (QBuffer *) 0 << 200 << QNetworkReply::NoError << QByteArray(); + QTest::newRow("connect") << QUrl("http://" + QtNetworkSettings::serverName()) << + QByteArray("CONNECT") << (QBuffer *) 0 << 400 << QNetworkReply::UnknownContentError << QByteArray(); // 400 = Bad Request + QTest::newRow("nonsense") << QUrl("http://" + QtNetworkSettings::serverName()) << + QByteArray("NONSENSE") << (QBuffer *) 0 << 501 << QNetworkReply::ProtocolUnknownError << QByteArray(); // 501 = Method Not Implemented + + QByteArray ba("test"); + QBuffer *buffer = new QBuffer; + buffer->setData(ba); + buffer->open(QIODevice::ReadOnly); + QTest::newRow("post") << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/md5sum.cgi") << QByteArray("POST") + << buffer << 200 << QNetworkReply::NoError << QByteArray("098f6bcd4621d373cade4e832627b4f6\n"); + + QByteArray ba2("test"); + QBuffer *buffer2 = new QBuffer; + buffer2->setData(ba2); + buffer2->open(QIODevice::ReadOnly); + QTest::newRow("put") << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/md5sum.cgi") << QByteArray("PUT") + << buffer2 << 200 << QNetworkReply::NoError << QByteArray("098f6bcd4621d373cade4e832627b4f6\n"); +} + +void tst_QNetworkReply::sendCustomRequestToHttp() +{ + QFETCH(QUrl, url); + QNetworkRequest request(url); + QNetworkReplyPtr reply; + QFETCH(QByteArray, verb); + QFETCH(QBuffer *, device); + runCustomRequest(request, reply, verb, device); + QCOMPARE(reply->url(), url); + QFETCH(QNetworkReply::NetworkError, error); + QCOMPARE(reply->error(), error); + QFETCH(int, resultCode); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), resultCode); + QFETCH(QByteArray, expectedContent); + if (! expectedContent.isEmpty()) + QCOMPARE(reply->readAll(), expectedContent); +} + +void tst_QNetworkReply::ioGetFromData_data() +{ + QTest::addColumn("urlStr"); + QTest::addColumn("data"); + + QTest::newRow("data-empty") << "data:," << QByteArray(); + QTest::newRow("data-literal") << "data:,foo" << QByteArray("foo"); + QTest::newRow("data-pct") << "data:,%3Cbody%20contentEditable%3Dtrue%3E%0D%0A" + << QByteArray("\r\n"); + QTest::newRow("data-base64") << "data:;base64,UXQgaXMgZ3JlYXQh" << QByteArray("Qt is great!"); +} + +void tst_QNetworkReply::ioGetFromData() +{ + QFETCH(QString, urlStr); + + QUrl url = QUrl::fromEncoded(urlStr.toLatin1()); + QNetworkRequest request(url); + + QNetworkReplyPtr reply = manager.get(request); + DataReader reader(reply); + + connect(reply, SIGNAL(finished()), + &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(reply->url(), request.url()); + QCOMPARE(reply->error(), QNetworkReply::NoError); + + QFETCH(QByteArray, data); + QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toInt(), data.size()); + QCOMPARE(reader.data.size(), data.size()); + QCOMPARE(reader.data, data); +} + +void tst_QNetworkReply::ioGetFromFileSpecial_data() +{ + getFromFileSpecial_data(); +} + +void tst_QNetworkReply::ioGetFromFileSpecial() +{ + QFETCH(QString, fileName); + QFETCH(QString, url); + + QFile resource(fileName); + QVERIFY(resource.open(QIODevice::ReadOnly)); + + QNetworkRequest request; + request.setUrl(url); + QNetworkReplyPtr reply = manager.get(request); + DataReader reader(reply); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(reply->url(), request.url()); + QCOMPARE(reply->error(), QNetworkReply::NoError); + + QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), resource.size()); + QCOMPARE(qint64(reader.data.size()), resource.size()); + QCOMPARE(reader.data, resource.readAll()); +} + +void tst_QNetworkReply::ioGetFromFile_data() +{ + putToFile_data(); +} + +void tst_QNetworkReply::ioGetFromFile() +{ + QTemporaryFile file(QDir::currentPath() + "/temp-XXXXXX"); + file.setAutoRemove(true); + QVERIFY(file.open()); + + QFETCH(QByteArray, data); + QVERIFY(file.write(data) == data.size()); + file.flush(); + QCOMPARE(file.size(), qint64(data.size())); + + QNetworkRequest request(QUrl::fromLocalFile(file.fileName())); + QNetworkReplyPtr reply = manager.get(request); + QVERIFY(reply->isFinished()); // a file should immediately be done + DataReader reader(reply); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(reply->url(), request.url()); + QCOMPARE(reply->error(), QNetworkReply::NoError); + + QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), file.size()); + QCOMPARE(qint64(reader.data.size()), file.size()); + QCOMPARE(reader.data, data); +} + +void tst_QNetworkReply::ioGetFromFtp_data() +{ + QTest::addColumn("fileName"); + QTest::addColumn("expectedSize"); + + QTest::newRow("bigfile") << "bigfile" << Q_INT64_C(519240); + + QFile file(SRCDIR "/rfc3252.txt"); + QTest::newRow("rfc3252.txt") << "rfc3252.txt" << file.size(); +} + +void tst_QNetworkReply::ioGetFromFtp() +{ + QFETCH(QString, fileName); + QFile reference(fileName); + reference.open(QIODevice::ReadOnly); // will fail for bigfile + + QNetworkRequest request("ftp://" + QtNetworkSettings::serverName() + "/qtest/" + fileName); + QNetworkReplyPtr reply = manager.get(request); + DataReader reader(reply); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(reply->url(), request.url()); + QCOMPARE(reply->error(), QNetworkReply::NoError); + + QFETCH(qint64, expectedSize); + QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), expectedSize); + QCOMPARE(qint64(reader.data.size()), expectedSize); + + if (reference.isOpen()) + QCOMPARE(reader.data, reference.readAll()); +} + +void tst_QNetworkReply::ioGetFromFtpWithReuse() +{ + QString fileName = SRCDIR "/rfc3252.txt"; + QFile reference(fileName); + reference.open(QIODevice::ReadOnly); + + QNetworkRequest request(QUrl("ftp://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt")); + + // two concurrent (actually, consecutive) gets: + QNetworkReplyPtr reply1 = manager.get(request); + DataReader reader1(reply1); + QNetworkReplyPtr reply2 = manager.get(request); + DataReader reader2(reply2); + QSignalSpy spy(reply1, SIGNAL(finished())); + + connect(reply2, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + if (spy.count() == 0) { + connect(reply1, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + } + + QCOMPARE(reply1->url(), request.url()); + QCOMPARE(reply2->url(), request.url()); + QCOMPARE(reply1->error(), QNetworkReply::NoError); + QCOMPARE(reply2->error(), QNetworkReply::NoError); + + QCOMPARE(qint64(reader1.data.size()), reference.size()); + QCOMPARE(qint64(reader2.data.size()), reference.size()); + QCOMPARE(reply1->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size()); + QCOMPARE(reply2->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size()); + + QByteArray referenceData = reference.readAll(); + QCOMPARE(reader1.data, referenceData); + QCOMPARE(reader2.data, referenceData); +} + +void tst_QNetworkReply::ioGetFromHttp() +{ + QFile reference(SRCDIR "/rfc3252.txt"); + QVERIFY(reference.open(QIODevice::ReadOnly)); + + QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt")); + QNetworkReplyPtr reply = manager.get(request); + DataReader reader(reply); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(reply->url(), request.url()); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + + QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size()); + QCOMPARE(qint64(reader.data.size()), reference.size()); + + QCOMPARE(reader.data, reference.readAll()); +} + +void tst_QNetworkReply::ioGetFromHttpWithReuseParallel() +{ + QFile reference(SRCDIR "/rfc3252.txt"); + QVERIFY(reference.open(QIODevice::ReadOnly)); + + QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt")); + QNetworkReplyPtr reply1 = manager.get(request); + QNetworkReplyPtr reply2 = manager.get(request); + DataReader reader1(reply1); + DataReader reader2(reply2); + QSignalSpy spy(reply1, SIGNAL(finished())); + + connect(reply2, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + if (spy.count() == 0) { + connect(reply1, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + } + + QCOMPARE(reply1->url(), request.url()); + QCOMPARE(reply2->url(), request.url()); + QCOMPARE(reply1->error(), QNetworkReply::NoError); + QCOMPARE(reply2->error(), QNetworkReply::NoError); + QCOMPARE(reply1->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + QCOMPARE(reply2->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + + QCOMPARE(reply1->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size()); + QCOMPARE(reply2->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size()); + QCOMPARE(qint64(reader1.data.size()), reference.size()); + QCOMPARE(qint64(reader2.data.size()), reference.size()); + + QByteArray referenceData = reference.readAll(); + QCOMPARE(reader1.data, referenceData); + QCOMPARE(reader2.data, referenceData); +} + +void tst_QNetworkReply::ioGetFromHttpWithReuseSequential() +{ + QFile reference(SRCDIR "/rfc3252.txt"); + QVERIFY(reference.open(QIODevice::ReadOnly)); + + QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt")); + { + QNetworkReplyPtr reply = manager.get(request); + DataReader reader(reply); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(reply->url(), request.url()); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + + QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size()); + QCOMPARE(qint64(reader.data.size()), reference.size()); + + QCOMPARE(reader.data, reference.readAll()); + } + + reference.seek(0); + // rinse and repeat: + { + QNetworkReplyPtr reply = manager.get(request); + DataReader reader(reply); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(reply->url(), request.url()); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + + QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size()); + QCOMPARE(qint64(reader.data.size()), reference.size()); + + QCOMPARE(reader.data, reference.readAll()); + } +} + +void tst_QNetworkReply::ioGetFromHttpWithAuth_data() +{ + QTest::addColumn("url"); + QTest::addColumn("expectedData"); + + QFile reference(SRCDIR "/rfc3252.txt"); + reference.open(QIODevice::ReadOnly); + QByteArray referenceData = reference.readAll(); + QTest::newRow("basic") << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfcs-auth/rfc3252.txt") << referenceData; + QTest::newRow("digest") << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/auth-digest/") << QByteArray("digest authentication successful\n"); +} + +void tst_QNetworkReply::ioGetFromHttpWithAuth() +{ + // This test sends three requests + // The first two in parallel + // The third after the first two finished + + QFETCH(QUrl, url); + QFETCH(QByteArray, expectedData); + QNetworkRequest request(url); + { + QNetworkReplyPtr reply1 = manager.get(request); + QNetworkReplyPtr reply2 = manager.get(request); + DataReader reader1(reply1); + DataReader reader2(reply2); + QSignalSpy finishedspy(reply1, SIGNAL(finished())); + + QSignalSpy authspy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*))); + connect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), + SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*))); + + connect(reply2, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + if (finishedspy.count() == 0) { + connect(reply1, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + } + manager.disconnect(SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), + this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*))); + + QCOMPARE(reply1->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + QCOMPARE(reply2->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + QCOMPARE(reader1.data, expectedData); + QCOMPARE(reader2.data, expectedData); + + QCOMPARE(authspy.count(), 1); + } + + // rinse and repeat: + { + QNetworkReplyPtr reply = manager.get(request); + DataReader reader(reply); + + QSignalSpy authspy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*))); + connect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), + SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*))); + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + manager.disconnect(SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), + this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*))); + + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + QCOMPARE(reader.data, expectedData); + + QCOMPARE(authspy.count(), 0); + } + + // now check with synchronous calls: + { + request.setAttribute( + QNetworkRequest::SynchronousRequestAttribute, + true); + + QSignalSpy authspy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*))); + QNetworkReplyPtr replySync = manager.get(request); + QVERIFY(replySync->isFinished()); // synchronous + QCOMPARE(authspy.count(), 0); + + // we cannot use a data reader here, since that connects to the readyRead signal, + // just use readAll() + + // the only thing we check here is that the auth cache was used when using synchronous requests + QCOMPARE(replySync->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + QCOMPARE(replySync->readAll(), expectedData); + } +} + +void tst_QNetworkReply::ioGetFromHttpWithAuthSynchronous() +{ + // verify that we do not enter an endless loop with synchronous calls and wrong credentials + // the case when we succeed with the login is tested in ioGetFromHttpWithAuth() + + QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfcs-auth/rfc3252.txt")); + request.setAttribute( + QNetworkRequest::SynchronousRequestAttribute, + true); + + QSignalSpy authspy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*))); + QNetworkReplyPtr replySync = manager.get(request); + QVERIFY(replySync->isFinished()); // synchronous + QCOMPARE(replySync->error(), QNetworkReply::AuthenticationRequiredError); + QCOMPARE(authspy.count(), 0); + QCOMPARE(replySync->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 401); +} + +void tst_QNetworkReply::ioGetFromHttpWithProxyAuth() +{ + qRegisterMetaType(); // for QSignalSpy + qRegisterMetaType(); + + // This test sends three requests + // The first two in parallel + // The third after the first two finished + QFile reference(SRCDIR "/rfc3252.txt"); + QVERIFY(reference.open(QIODevice::ReadOnly)); + + QNetworkProxy proxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129); + QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt")); + { + manager.setProxy(proxy); + QNetworkReplyPtr reply1 = manager.get(request); + QNetworkReplyPtr reply2 = manager.get(request); + manager.setProxy(QNetworkProxy()); + + DataReader reader1(reply1); + DataReader reader2(reply2); + QSignalSpy finishedspy(reply1, SIGNAL(finished())); + + QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + connect(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + + connect(reply2, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + if (finishedspy.count() == 0) { + connect(reply1, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + } + manager.disconnect(SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + + QCOMPARE(reply1->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + QCOMPARE(reply2->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + QByteArray referenceData = reference.readAll(); + QCOMPARE(reader1.data, referenceData); + QCOMPARE(reader2.data, referenceData); + + QCOMPARE(authspy.count(), 1); + } + + reference.seek(0); + // rinse and repeat: + { + manager.setProxy(proxy); + QNetworkReplyPtr reply = manager.get(request); + DataReader reader(reply); + manager.setProxy(QNetworkProxy()); + + QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + connect(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + manager.disconnect(SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + QCOMPARE(reader.data, reference.readAll()); + + QCOMPARE(authspy.count(), 0); + } + + // now check with synchronous calls: + reference.seek(0); + { + request.setAttribute( + QNetworkRequest::SynchronousRequestAttribute, + true); + + QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + QNetworkReplyPtr replySync = manager.get(request); + QVERIFY(replySync->isFinished()); // synchronous + QCOMPARE(authspy.count(), 0); + + // we cannot use a data reader here, since that connects to the readyRead signal, + // just use readAll() + + // the only thing we check here is that the proxy auth cache was used when using synchronous requests + QCOMPARE(replySync->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + QCOMPARE(replySync->readAll(), reference.readAll()); + } +} + +void tst_QNetworkReply::ioGetFromHttpWithProxyAuthSynchronous() +{ + // verify that we do not enter an endless loop with synchronous calls and wrong credentials + // the case when we succeed with the login is tested in ioGetFromHttpWithAuth() + + QNetworkProxy proxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129); + QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt")); + manager.setProxy(proxy); + request.setAttribute( + QNetworkRequest::SynchronousRequestAttribute, + true); + + QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + QNetworkReplyPtr replySync = manager.get(request); + manager.setProxy(QNetworkProxy()); // reset + QVERIFY(replySync->isFinished()); // synchronous + QCOMPARE(replySync->error(), QNetworkReply::ProxyAuthenticationRequiredError); + QCOMPARE(authspy.count(), 0); + QCOMPARE(replySync->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 407); +} + +void tst_QNetworkReply::ioGetFromHttpWithSocksProxy() +{ + // HTTP caching proxies are tested by the above function + // test SOCKSv5 proxies too + + qRegisterMetaType(); // for QSignalSpy + qRegisterMetaType(); + + QFile reference(SRCDIR "/rfc3252.txt"); + QVERIFY(reference.open(QIODevice::ReadOnly)); + + QNetworkProxy proxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080); + QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt")); + { + manager.setProxy(proxy); + QNetworkReplyPtr reply = manager.get(request); + DataReader reader(reply); + manager.setProxy(QNetworkProxy()); + + QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + connect(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + manager.disconnect(SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + QCOMPARE(reader.data, reference.readAll()); + + QCOMPARE(authspy.count(), 0); + } + + // set an invalid proxy just to make sure that we can't load + proxy = QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1079); + { + manager.setProxy(proxy); + QNetworkReplyPtr reply = manager.get(request); + DataReader reader(reply); + manager.setProxy(QNetworkProxy()); + + QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + connect(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + manager.disconnect(SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + + QVERIFY(!reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).isValid()); + QVERIFY(reader.data.isEmpty()); + + QVERIFY(int(reply->error()) > 0); + QEXPECT_FAIL("", "QTcpSocket doesn't return enough information yet", Continue); + QCOMPARE(int(reply->error()), int(QNetworkReply::ProxyConnectionRefusedError)); + + QCOMPARE(authspy.count(), 0); + } +} + +#ifndef QT_NO_OPENSSL +void tst_QNetworkReply::ioGetFromHttpsWithSslErrors() +{ + qRegisterMetaType(); // for QSignalSpy + qRegisterMetaType >(); + + QFile reference(SRCDIR "/rfc3252.txt"); + QVERIFY(reference.open(QIODevice::ReadOnly)); + + QNetworkRequest request(QUrl("https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt")); + QNetworkReplyPtr reply = manager.get(request); + DataReader reader(reply); + + QSignalSpy sslspy(&manager, SIGNAL(sslErrors(QNetworkReply*,QList))); + connect(&manager, SIGNAL(sslErrors(QNetworkReply*,QList)), + SLOT(sslErrors(QNetworkReply*,QList))); + connect(reply, SIGNAL(metaDataChanged()), SLOT(storeSslConfiguration())); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + manager.disconnect(SIGNAL(sslErrors(QNetworkReply*,QList)), + this, SLOT(sslErrors(QNetworkReply*,QList))); + + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + QCOMPARE(reader.data, reference.readAll()); + + QCOMPARE(sslspy.count(), 1); + + QVERIFY(!storedSslConfiguration.isNull()); + QVERIFY(!reply->sslConfiguration().isNull()); +} + +void tst_QNetworkReply::ioGetFromHttpsWithIgnoreSslErrors() +{ + // same as above, except that we call ignoreSslErrors and don't connect + // to the sslErrors() signal (which is *still* emitted) + + qRegisterMetaType(); // for QSignalSpy + qRegisterMetaType >(); + + QFile reference(SRCDIR "/rfc3252.txt"); + QVERIFY(reference.open(QIODevice::ReadOnly)); + + QNetworkRequest request(QUrl("https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt")); + + QNetworkReplyPtr reply = manager.get(request); + reply->ignoreSslErrors(); + DataReader reader(reply); + + QSignalSpy sslspy(&manager, SIGNAL(sslErrors(QNetworkReply*,QList))); + connect(reply, SIGNAL(metaDataChanged()), SLOT(storeSslConfiguration())); + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + QCOMPARE(reader.data, reference.readAll()); + + QCOMPARE(sslspy.count(), 1); + + QVERIFY(!storedSslConfiguration.isNull()); + QVERIFY(!reply->sslConfiguration().isNull()); +} + +void tst_QNetworkReply::ioGetFromHttpsWithSslHandshakeError() +{ + qRegisterMetaType(); // for QSignalSpy + qRegisterMetaType >(); + + QFile reference(SRCDIR "/rfc3252.txt"); + QVERIFY(reference.open(QIODevice::ReadOnly)); + + QNetworkRequest request(QUrl("https://" + QtNetworkSettings::serverName() + ":80")); + + QNetworkReplyPtr reply = manager.get(request); + reply->ignoreSslErrors(); + DataReader reader(reply); + + QSignalSpy sslspy(&manager, SIGNAL(sslErrors(QNetworkReply*,QList))); + connect(reply, SIGNAL(metaDataChanged()), SLOT(storeSslConfiguration())); + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(reply->error(), QNetworkReply::SslHandshakeFailedError); + QCOMPARE(sslspy.count(), 0); +} +#endif + +void tst_QNetworkReply::ioGetFromHttpBrokenServer_data() +{ + QTest::addColumn("dataToSend"); + QTest::addColumn("doDisconnect"); + + QTest::newRow("no-newline") << QByteArray("Hello World") << false; + + // these are OK now, we just eat the lonely newlines + //QTest::newRow("just-newline") << QByteArray("\r\n") << false; + //QTest::newRow("just-2newline") << QByteArray("\r\n\r\n") << false; + + QTest::newRow("with-newlines") << QByteArray("Long first line\r\nLong second line") << false; + QTest::newRow("with-newlines2") << QByteArray("\r\nSecond line") << false; + QTest::newRow("with-newlines3") << QByteArray("ICY\r\nSecond line") << false; + QTest::newRow("invalid-version") << QByteArray("HTTP/123 200 \r\n") << false; + QTest::newRow("invalid-version2") << QByteArray("HTTP/a.\033 200 \r\n") << false; + QTest::newRow("invalid-reply-code") << QByteArray("HTTP/1.0 fuu \r\n") << false; + + QTest::newRow("empty+disconnect") << QByteArray() << true; + + QTest::newRow("no-newline+disconnect") << QByteArray("Hello World") << true; + QTest::newRow("just-newline+disconnect") << QByteArray("\r\n") << true; + QTest::newRow("just-2newline+disconnect") << QByteArray("\r\n\r\n") << true; + QTest::newRow("with-newlines+disconnect") << QByteArray("Long first line\r\nLong second line") << true; + QTest::newRow("with-newlines2+disconnect") << QByteArray("\r\nSecond line") << true; + QTest::newRow("with-newlines3+disconnect") << QByteArray("ICY\r\nSecond line") << true; + + QTest::newRow("invalid-version+disconnect") << QByteArray("HTTP/123 200 ") << true; + QTest::newRow("invalid-version2+disconnect") << QByteArray("HTTP/a.\033 200 ") << true; + QTest::newRow("invalid-reply-code+disconnect") << QByteArray("HTTP/1.0 fuu ") << true; + + QTest::newRow("immediate disconnect") << QByteArray("") << true; + QTest::newRow("justHalfStatus+disconnect") << QByteArray("HTTP/1.1") << true; + QTest::newRow("justStatus+disconnect") << QByteArray("HTTP/1.1 200 OK\r\n") << true; + QTest::newRow("justStatusAndHalfHeaders+disconnect") << QByteArray("HTTP/1.1 200 OK\r\nContent-L") << true; + + QTest::newRow("halfContent+disconnect") << QByteArray("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\nAB") << true; + +} + +void tst_QNetworkReply::ioGetFromHttpBrokenServer() +{ + QFETCH(QByteArray, dataToSend); + QFETCH(bool, doDisconnect); + MiniHttpServer server(dataToSend); + server.doClose = doDisconnect; + + QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort()))); + QNetworkReplyPtr reply = manager.get(request); + QSignalSpy spy(reply, SIGNAL(error(QNetworkReply::NetworkError))); + + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(reply->url(), request.url()); + QCOMPARE(spy.count(), 1); + QVERIFY(reply->error() != QNetworkReply::NoError); +} + +void tst_QNetworkReply::ioGetFromHttpStatus100_data() +{ + QTest::addColumn("dataToSend"); + QTest::addColumn("statusCode"); + QTest::newRow("normal") << QByteArray("HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200; + QTest::newRow("minimal") << QByteArray("HTTP/1.1 100 Continue\n\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200; + QTest::newRow("minimal2") << QByteArray("HTTP/1.1 100 Continue\n\nHTTP/1.0 200 OK\r\n\r\n") << 200; + QTest::newRow("minimal3") << QByteArray("HTTP/1.1 100 Continue\n\nHTTP/1.0 200 OK\n\n") << 200; + QTest::newRow("minimal+404") << QByteArray("HTTP/1.1 100 Continue\n\nHTTP/1.0 204 No Content\r\n\r\n") << 204; + QTest::newRow("with_headers") << QByteArray("HTTP/1.1 100 Continue\r\nBla: x\r\n\r\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200; + QTest::newRow("with_headers2") << QByteArray("HTTP/1.1 100 Continue\nBla: x\n\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200; +} + +void tst_QNetworkReply::ioGetFromHttpStatus100() +{ + QFETCH(QByteArray, dataToSend); + QFETCH(int, statusCode); + MiniHttpServer server(dataToSend); + server.doClose = true; + + QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort()))); + QNetworkReplyPtr reply = manager.get(request); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(reply->url(), request.url()); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), statusCode); + QVERIFY(reply->rawHeader("bla").isNull()); +} + +void tst_QNetworkReply::ioGetFromHttpNoHeaders_data() +{ + QTest::addColumn("dataToSend"); + QTest::newRow("justStatus+noheaders+disconnect") << QByteArray("HTTP/1.0 200 OK\r\n\r\n"); +} + +void tst_QNetworkReply::ioGetFromHttpNoHeaders() +{ + QFETCH(QByteArray, dataToSend); + MiniHttpServer server(dataToSend); + server.doClose = true; + + QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort()))); + QNetworkReplyPtr reply = manager.get(request); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(reply->url(), request.url()); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); +} + +void tst_QNetworkReply::ioGetFromHttpWithCache_data() +{ + qRegisterMetaType(); + QTest::addColumn("dataToSend"); + QTest::addColumn("body"); + QTest::addColumn("cachedReply"); + QTest::addColumn("cacheMode"); + QTest::addColumn("extraHttpHeaders"); + QTest::addColumn("loadedFromCache"); + QTest::addColumn("networkUsed"); + + QByteArray reply200 = + "HTTP/1.0 200\r\n" + "Connection: keep-alive\r\n" + "Content-Type: text/plain\r\n" + "Cache-control: no-cache\r\n" + "Content-length: 8\r\n" + "\r\n" + "Reloaded"; + QByteArray reply304 = + "HTTP/1.0 304 Use Cache\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + + QTest::newRow("not-cached,always-network") + << reply200 << "Reloaded" << MyMemoryCache::CachedContent() << int(QNetworkRequest::AlwaysNetwork) << QStringList() << false << true; + QTest::newRow("not-cached,prefer-network") + << reply200 << "Reloaded" << MyMemoryCache::CachedContent() << int(QNetworkRequest::PreferNetwork) << QStringList() << false << true; + QTest::newRow("not-cached,prefer-cache") + << reply200 << "Reloaded" << MyMemoryCache::CachedContent() << int(QNetworkRequest::PreferCache) << QStringList() << false << true; + + QDateTime present = QDateTime::currentDateTime().toUTC(); + QDateTime past = present.addSecs(-3600); + QDateTime future = present.addSecs(3600); + static const char dateFormat[] = "ddd, dd MMM yyyy hh:mm:ss 'GMT'"; + + QNetworkCacheMetaData::RawHeaderList rawHeaders; + MyMemoryCache::CachedContent content; + content.second = "Not-reloaded"; + content.first.setLastModified(past); + + // + // Set to expired + // + rawHeaders.clear(); + rawHeaders << QNetworkCacheMetaData::RawHeader("Date", QLocale::c().toString(past, dateFormat).toLatin1()) + << QNetworkCacheMetaData::RawHeader("Cache-control", "max-age=0"); // isn't used in cache loading + content.first.setRawHeaders(rawHeaders); + content.first.setLastModified(past); + + QTest::newRow("expired,200,prefer-network") + << reply200 << "Reloaded" << content << int(QNetworkRequest::PreferNetwork) << QStringList() << false << true; + QTest::newRow("expired,200,prefer-cache") + << reply200 << "Reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << false << true; + + QTest::newRow("expired,304,prefer-network") + << reply304 << "Not-reloaded" << content << int(QNetworkRequest::PreferNetwork) << QStringList() << true << true; + QTest::newRow("expired,304,prefer-cache") + << reply304 << "Not-reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << true << true; + + // + // Set to not-expired + // + rawHeaders.clear(); + rawHeaders << QNetworkCacheMetaData::RawHeader("Date", QLocale::c().toString(past, dateFormat).toLatin1()) + << QNetworkCacheMetaData::RawHeader("Cache-control", "max-age=7200"); // isn't used in cache loading + content.first.setRawHeaders(rawHeaders); + content.first.setExpirationDate(future); + + QTest::newRow("not-expired,200,always-network") + << reply200 << "Reloaded" << content << int(QNetworkRequest::AlwaysNetwork) << QStringList() << false << true; + QTest::newRow("not-expired,200,prefer-network") + << reply200 << "Not-reloaded" << content << int(QNetworkRequest::PreferNetwork) << QStringList() << true << false; + QTest::newRow("not-expired,200,prefer-cache") + << reply200 << "Not-reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << true << false; + QTest::newRow("not-expired,200,always-cache") + << reply200 << "Not-reloaded" << content << int(QNetworkRequest::AlwaysCache) << QStringList() << true << false; + + QTest::newRow("not-expired,304,prefer-network") + << reply304 << "Not-reloaded" << content << int(QNetworkRequest::PreferNetwork) << QStringList() << true << false; + QTest::newRow("not-expired,304,prefer-cache") + << reply304 << "Not-reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << true << false; + QTest::newRow("not-expired,304,always-cache") + << reply304 << "Not-reloaded" << content << int(QNetworkRequest::AlwaysCache) << QStringList() << true << false; + + // + // Set must-revalidate now + // + rawHeaders.clear(); + rawHeaders << QNetworkCacheMetaData::RawHeader("Date", QLocale::c().toString(past, dateFormat).toLatin1()) + << QNetworkCacheMetaData::RawHeader("Cache-control", "max-age=7200, must-revalidate"); // must-revalidate is used + content.first.setRawHeaders(rawHeaders); + + QTest::newRow("must-revalidate,200,always-network") + << reply200 << "Reloaded" << content << int(QNetworkRequest::AlwaysNetwork) << QStringList() << false << true; + QTest::newRow("must-revalidate,200,prefer-network") + << reply200 << "Reloaded" << content << int(QNetworkRequest::PreferNetwork) << QStringList() << false << true; + QTest::newRow("must-revalidate,200,prefer-cache") + << reply200 << "Reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << false << true; + QTest::newRow("must-revalidate,200,always-cache") + << reply200 << "" << content << int(QNetworkRequest::AlwaysCache) << QStringList() << false << false; + + QTest::newRow("must-revalidate,304,prefer-network") + << reply304 << "Not-reloaded" << content << int(QNetworkRequest::PreferNetwork) << QStringList() << true << true; + QTest::newRow("must-revalidate,304,prefer-cache") + << reply304 << "Not-reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << true << true; + QTest::newRow("must-revalidate,304,always-cache") + << reply304 << "" << content << int(QNetworkRequest::AlwaysCache) << QStringList() << false << false; + + // + // Partial content + // + rawHeaders.clear(); + rawHeaders << QNetworkCacheMetaData::RawHeader("Date", QLocale::c().toString(past, dateFormat).toLatin1()) + << QNetworkCacheMetaData::RawHeader("Cache-control", "max-age=7200"); // isn't used in cache loading + content.first.setRawHeaders(rawHeaders); + content.first.setExpirationDate(future); + + QByteArray reply206 = + "HTTP/1.0 206\r\n" + "Connection: keep-alive\r\n" + "Content-Type: text/plain\r\n" + "Cache-control: no-cache\r\n" + "Content-Range: bytes 2-6/8\r\n" + "Content-length: 4\r\n" + "\r\n" + "load"; + + QTest::newRow("partial,dontuse-cache") + << reply206 << "load" << content << int(QNetworkRequest::PreferCache) << (QStringList() << "Range" << "bytes=2-6") << false << true; +} + +void tst_QNetworkReply::ioGetFromHttpWithCache() +{ + QFETCH(QByteArray, dataToSend); + MiniHttpServer server(dataToSend); + server.doClose = false; + + MyMemoryCache *memoryCache = new MyMemoryCache(&manager); + manager.setCache(memoryCache); + + QFETCH(MyMemoryCache::CachedContent, cachedReply); + QUrl url = "http://localhost:" + QString::number(server.serverPort()); + cachedReply.first.setUrl(url); + if (!cachedReply.second.isNull()) + memoryCache->cache.insert(url.toEncoded(), cachedReply); + + QFETCH(int, cacheMode); + QNetworkRequest request(url); + request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, cacheMode); + request.setAttribute(QNetworkRequest::CacheSaveControlAttribute, false); + + QFETCH(QStringList, extraHttpHeaders); + QStringListIterator it(extraHttpHeaders); + while (it.hasNext()) { + QString header = it.next(); + QString value = it.next(); + request.setRawHeader(header.toLatin1(), value.toLatin1()); // To latin1? Deal with it! + } + + QNetworkReplyPtr reply = manager.get(request); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QTEST(reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool(), "loadedFromCache"); + QTEST(server.totalConnections > 0, "networkUsed"); + QFETCH(QString, body); + QCOMPARE(reply->readAll().constData(), qPrintable(body)); +} + +void tst_QNetworkReply::ioGetWithManyProxies_data() +{ + QTest::addColumn >("proxyList"); + QTest::addColumn("proxyUsed"); + QTest::addColumn("url"); + QTest::addColumn("expectedError"); + + QList proxyList; + + // All of the other functions test DefaultProxy + // So let's test something else + + // Simple tests that work: + + // HTTP request with HTTP caching proxy + proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129); + QTest::newRow("http-on-http") + << proxyList << proxyList.at(0) + << "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt" + << QNetworkReply::NoError; + + // HTTP request with HTTP transparent proxy + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3129); + QTest::newRow("http-on-http2") + << proxyList << proxyList.at(0) + << "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt" + << QNetworkReply::NoError; + + // HTTP request with SOCKS transparent proxy + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1081); + QTest::newRow("http-on-socks") + << proxyList << proxyList.at(0) + << "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt" + << QNetworkReply::NoError; + + // FTP request with FTP caching proxy + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121); + QTest::newRow("ftp-on-ftp") + << proxyList << proxyList.at(0) + << "ftp://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt" + << QNetworkReply::NoError; + + // The following test doesn't work because QFtp is too limited + // It can only talk to its own kind of proxies + + // FTP request with SOCKSv5 transparent proxy + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1081); + QTest::newRow("ftp-on-socks") + << proxyList << proxyList.at(0) + << "ftp://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt" + << QNetworkReply::NoError; + +#ifndef QT_NO_OPENSSL + // HTTPS with HTTP transparent proxy + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3129); + QTest::newRow("https-on-http") + << proxyList << proxyList.at(0) + << "https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt" + << QNetworkReply::NoError; + + // HTTPS request with SOCKS transparent proxy + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1081); + QTest::newRow("https-on-socks") + << proxyList << proxyList.at(0) + << "https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt" + << QNetworkReply::NoError; +#endif + + // Tests that fail: + + // HTTP request with FTP caching proxy + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121); + QTest::newRow("http-on-ftp") + << proxyList << QNetworkProxy() + << "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt" + << QNetworkReply::ProxyNotFoundError; + + // FTP request with HTTP caching proxy + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129); + QTest::newRow("ftp-on-http") + << proxyList << QNetworkProxy() + << "ftp://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt" + << QNetworkReply::ProxyNotFoundError; + + // FTP request with HTTP caching proxies + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129) + << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3130); + QTest::newRow("ftp-on-multiple-http") + << proxyList << QNetworkProxy() + << "ftp://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt" + << QNetworkReply::ProxyNotFoundError; + +#ifndef QT_NO_OPENSSL + // HTTPS with HTTP caching proxy + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129); + QTest::newRow("https-on-httptransparent") + << proxyList << QNetworkProxy() + << "https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt" + << QNetworkReply::ProxyNotFoundError; + + // HTTPS with FTP caching proxy + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121); + QTest::newRow("https-on-ftp") + << proxyList << QNetworkProxy() + << "https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt" + << QNetworkReply::ProxyNotFoundError; +#endif + + // Complex requests: + + // HTTP request with more than one HTTP proxy + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129) + << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3130); + QTest::newRow("http-on-multiple-http") + << proxyList << proxyList.at(0) + << "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt" + << QNetworkReply::NoError; + + // HTTP request with HTTP + SOCKS + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129) + << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1081); + QTest::newRow("http-on-http+socks") + << proxyList << proxyList.at(0) + << "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt" + << QNetworkReply::NoError; + + // HTTP request with FTP + HTTP + SOCKS + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121) + << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129) + << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1081); + QTest::newRow("http-on-ftp+http+socks") + << proxyList << proxyList.at(1) // second proxy should be used + << "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt" + << QNetworkReply::NoError; + + // HTTP request with NoProxy + HTTP + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::NoProxy) + << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129); + QTest::newRow("http-on-noproxy+http") + << proxyList << proxyList.at(0) + << "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt" + << QNetworkReply::NoError; + + // HTTP request with FTP + NoProxy + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121) + << QNetworkProxy(QNetworkProxy::NoProxy); + QTest::newRow("http-on-ftp+noproxy") + << proxyList << proxyList.at(1) // second proxy should be used + << "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt" + << QNetworkReply::NoError; + + // FTP request with HTTP Caching + FTP + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129) + << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121); + QTest::newRow("ftp-on-http+ftp") + << proxyList << proxyList.at(1) // second proxy should be used + << "ftp://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt" + << QNetworkReply::NoError; + +#ifndef QT_NO_OPENSSL + // HTTPS request with HTTP Caching + HTTP transparent + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129) + << QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3129); + QTest::newRow("https-on-httpcaching+http") + << proxyList << proxyList.at(1) // second proxy should be used + << "https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt" + << QNetworkReply::NoError; + + // HTTPS request with FTP + HTTP C + HTTP T + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121) + << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129) + << QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3129); + QTest::newRow("https-on-ftp+httpcaching+http") + << proxyList << proxyList.at(2) // skip the first two + << "https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt" + << QNetworkReply::NoError; +#endif +} + +void tst_QNetworkReply::ioGetWithManyProxies() +{ + // Test proxy factories + + qRegisterMetaType(); // for QSignalSpy + qRegisterMetaType(); + + QFile reference(SRCDIR "/rfc3252.txt"); + QVERIFY(reference.open(QIODevice::ReadOnly)); + + // set the proxy factory: + QFETCH(QList, proxyList); + MyProxyFactory *proxyFactory = new MyProxyFactory; + proxyFactory->toReturn = proxyList; + manager.setProxyFactory(proxyFactory); + + QFETCH(QString, url); + QUrl theUrl(url); + QNetworkRequest request(theUrl); + QNetworkReplyPtr reply = manager.get(request); + DataReader reader(reply); + + QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + connect(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); +#ifndef QT_NO_OPENSSL + connect(&manager, SIGNAL(sslErrors(QNetworkReply*,QList)), + SLOT(sslErrors(QNetworkReply*,QList))); +#endif + QTestEventLoop::instance().enterLoop(15); + QVERIFY(!QTestEventLoop::instance().timeout()); + + manager.disconnect(SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); +#ifndef QT_NO_OPENSSL + manager.disconnect(SIGNAL(sslErrors(QNetworkReply*,QList)), + this, SLOT(sslErrors(QNetworkReply*,QList))); +#endif + + QFETCH(QNetworkReply::NetworkError, expectedError); + QEXPECT_FAIL("ftp-on-socks", "QFtp is too limited and won't accept non-FTP proxies", Abort); + QCOMPARE(reply->error(), expectedError); + + // Verify that the factory was called properly + QCOMPARE(proxyFactory->callCount, 1); + QCOMPARE(proxyFactory->lastQuery, QNetworkProxyQuery(theUrl)); + + if (expectedError == QNetworkReply::NoError) { + // request succeeded + QCOMPARE(reader.data, reference.readAll()); + + // now verify that the proxies worked: + QFETCH(QNetworkProxy, proxyUsed); + if (proxyUsed.type() == QNetworkProxy::NoProxy) { + QCOMPARE(authspy.count(), 0); + } else { + if (QByteArray(QTest::currentDataTag()).startsWith("ftp-")) + return; // No authentication with current FTP or with FTP proxies + QCOMPARE(authspy.count(), 1); + QCOMPARE(qvariant_cast(authspy.at(0).at(0)), proxyUsed); + } + } else { + // request failed + QCOMPARE(authspy.count(), 0); + } +} + +void tst_QNetworkReply::ioPutToFileFromFile_data() +{ + QTest::addColumn("fileName"); + + QTest::newRow("empty") << SRCDIR "/empty"; + QTest::newRow("real-file") << SRCDIR "/rfc3252.txt"; + QTest::newRow("resource") << ":/resource"; + QTest::newRow("search-path") << "srcdir:/rfc3252.txt"; +} + +void tst_QNetworkReply::ioPutToFileFromFile() +{ + QFETCH(QString, fileName); + QFile sourceFile(fileName); + QFile targetFile(testFileName); + + QVERIFY(sourceFile.open(QIODevice::ReadOnly)); + + QUrl url = QUrl::fromLocalFile(targetFile.fileName()); + QNetworkRequest request(url); + QNetworkReplyPtr reply = manager.put(request, &sourceFile); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0)); + QVERIFY(reply->readAll().isEmpty()); + + QVERIFY(sourceFile.atEnd()); + sourceFile.seek(0); // reset it to the beginning + + QVERIFY(targetFile.open(QIODevice::ReadOnly)); + QCOMPARE(targetFile.size(), sourceFile.size()); + QCOMPARE(targetFile.readAll(), sourceFile.readAll()); +} + +void tst_QNetworkReply::ioPutToFileFromSocket_data() +{ + putToFile_data(); +} + +void tst_QNetworkReply::ioPutToFileFromSocket() +{ + QFile file(testFileName); + + QUrl url = QUrl::fromLocalFile(file.fileName()); + QNetworkRequest request(url); + + QFETCH(QByteArray, data); + SocketPair socketpair; + socketpair.create(); + QVERIFY(socketpair.endPoints[0] && socketpair.endPoints[1]); + + socketpair.endPoints[0]->write(data); + QNetworkReplyPtr reply = manager.put(QNetworkRequest(url), socketpair.endPoints[1]); + socketpair.endPoints[0]->close(); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0)); + QVERIFY(reply->readAll().isEmpty()); + + QVERIFY(file.open(QIODevice::ReadOnly)); + QCOMPARE(file.size(), qint64(data.size())); + QByteArray contents = file.readAll(); + QCOMPARE(contents, data); +} + +void tst_QNetworkReply::ioPutToFileFromLocalSocket_data() +{ + putToFile_data(); +} + +void tst_QNetworkReply::ioPutToFileFromLocalSocket() +{ + QString socketname = "networkreplytest"; + QLocalServer server; + if (!server.listen(socketname)) { + QLocalServer::removeServer(socketname); + QVERIFY(server.listen(socketname)); + } + QLocalSocket active; + active.connectToServer(socketname); + QVERIFY2(server.waitForNewConnection(10), server.errorString().toLatin1().constData()); + QVERIFY2(active.waitForConnected(10), active.errorString().toLatin1().constData()); + QVERIFY2(server.hasPendingConnections(), server.errorString().toLatin1().constData()); + QLocalSocket *passive = server.nextPendingConnection(); + + QFile file(testFileName); + QUrl url = QUrl::fromLocalFile(file.fileName()); + QNetworkRequest request(url); + + QFETCH(QByteArray, data); + active.write(data); + active.close(); + QNetworkReplyPtr reply = manager.put(QNetworkRequest(url), passive); + passive->setParent(reply); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0)); + QVERIFY(reply->readAll().isEmpty()); + + QVERIFY(file.open(QIODevice::ReadOnly)); + QCOMPARE(file.size(), qint64(data.size())); + QByteArray contents = file.readAll(); + QCOMPARE(contents, data); +} + +void tst_QNetworkReply::ioPutToFileFromProcess_data() +{ + putToFile_data(); +} + +void tst_QNetworkReply::ioPutToFileFromProcess() +{ +#if defined(Q_OS_WINCE) || defined (Q_OS_SYMBIAN) + QSKIP("Currently no stdin/out supported for Windows CE / Symbian OS", SkipAll); +#else + +#ifdef Q_OS_WIN + if (qstrcmp(QTest::currentDataTag(), "small") == 0) + QSKIP("When passing a CR-LF-LF sequence through Windows stdio, it gets converted, " + "so this test fails. Disabled on Windows", SkipSingle); +#endif + +#if defined(QT_NO_PROCESS) + QSKIP("Qt was compiled with QT_NO_PROCESS", SkipAll); +#else + QFile file(testFileName); + + QUrl url = QUrl::fromLocalFile(file.fileName()); + QNetworkRequest request(url); + + QFETCH(QByteArray, data); + QProcess process; + process.start("echo/echo all"); + process.write(data); + process.closeWriteChannel(); + + QNetworkReplyPtr reply = manager.put(QNetworkRequest(url), &process); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0)); + QVERIFY(reply->readAll().isEmpty()); + + QVERIFY(file.open(QIODevice::ReadOnly)); + QCOMPARE(file.size(), qint64(data.size())); + QByteArray contents = file.readAll(); + QCOMPARE(contents, data); +#endif +#endif +} + +void tst_QNetworkReply::ioPutToFtpFromFile_data() +{ + ioPutToFileFromFile_data(); +} + +void tst_QNetworkReply::ioPutToFtpFromFile() +{ + QFETCH(QString, fileName); + QFile sourceFile(fileName); + QVERIFY(sourceFile.open(QIODevice::ReadOnly)); + + QUrl url("ftp://" + QtNetworkSettings::serverName()); + url.setPath(QString("/qtest/upload/qnetworkaccess-ioPutToFtpFromFile-%1-%2") + .arg(QTest::currentDataTag()) + .arg(uniqueExtension)); + + QNetworkRequest request(url); + QNetworkReplyPtr reply = manager.put(request, &sourceFile); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0)); + QVERIFY(reply->readAll().isEmpty()); + + QVERIFY(sourceFile.atEnd()); + sourceFile.seek(0); // reset it to the beginning + + // download the file again from FTP to make sure it was uploaded + // correctly + QFtp ftp; + ftp.connectToHost(url.host()); + ftp.login(); + ftp.get(url.path()); + + QObject::connect(&ftp, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(3); + QObject::disconnect(&ftp, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop())); + + QByteArray uploaded = ftp.readAll(); + QCOMPARE(qint64(uploaded.size()), sourceFile.size()); + QCOMPARE(uploaded, sourceFile.readAll()); + + ftp.close(); + QObject::connect(&ftp, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QObject::disconnect(&ftp, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop())); +} + +void tst_QNetworkReply::ioPutToHttpFromFile_data() +{ + ioPutToFileFromFile_data(); +} + +void tst_QNetworkReply::ioPutToHttpFromFile() +{ + QFETCH(QString, fileName); + QFile sourceFile(fileName); + QVERIFY(sourceFile.open(QIODevice::ReadOnly)); + + QUrl url("http://" + QtNetworkSettings::serverName()); + url.setPath(QString("/dav/qnetworkaccess-ioPutToHttpFromFile-%1-%2") + .arg(QTest::currentDataTag()) + .arg(uniqueExtension)); + + QNetworkRequest request(url); + QNetworkReplyPtr reply = manager.put(request, &sourceFile); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::NoError); + + // verify that the HTTP status code is 201 Created + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 201); + + QVERIFY(sourceFile.atEnd()); + sourceFile.seek(0); // reset it to the beginning + + // download the file again from HTTP to make sure it was uploaded + // correctly + reply = manager.get(request); + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok + + QCOMPARE(reply->readAll(), sourceFile.readAll()); +} + +void tst_QNetworkReply::ioPostToHttpFromFile_data() +{ + ioPutToFileFromFile_data(); +} + +void tst_QNetworkReply::ioPostToHttpFromFile() +{ + QFETCH(QString, fileName); + QFile sourceFile(fileName); + QVERIFY(sourceFile.open(QIODevice::ReadOnly)); + + QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/md5sum.cgi"); + QNetworkRequest request(url); + request.setRawHeader("Content-Type", "application/octet-stream"); + + QNetworkReplyPtr reply = manager.post(request, &sourceFile); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::NoError); + + // verify that the HTTP status code is 200 Ok + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + + QVERIFY(sourceFile.atEnd()); + sourceFile.seek(0); // reset it to the beginning + + QCOMPARE(reply->readAll().trimmed(), md5sum(sourceFile.readAll()).toHex()); +} + +void tst_QNetworkReply::ioPostToHttpFromSocket_data() +{ + QTest::addColumn("data"); + QTest::addColumn("md5sum"); + QTest::addColumn("url"); + QTest::addColumn("proxy"); + QTest::addColumn("authenticationRequiredCount"); + QTest::addColumn("proxyAuthenticationRequiredCount"); + + for (int i = 0; i < proxies.count(); ++i) + for (int auth = 0; auth < 2; ++auth) { + QUrl url; + if (auth) + url = "http://" + QtNetworkSettings::serverName() + "/qtest/protected/cgi-bin/md5sum.cgi"; + else + url = "http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/md5sum.cgi"; + + QNetworkProxy proxy = proxies.at(i).proxy; + QByteArray testsuffix = QByteArray(auth ? "+auth" : "") + proxies.at(i).tag; + int proxyauthcount = proxies.at(i).requiresAuthentication; + + QByteArray data; + data = ""; + QTest::newRow("empty" + testsuffix) << data << md5sum(data) << url << proxy << auth << proxyauthcount; + + data = "This is a normal message."; + QTest::newRow("generic" + testsuffix) << data << md5sum(data) << url << proxy << auth << proxyauthcount; + + data = "This is a message to show that Qt rocks!\r\n\n"; + QTest::newRow("small" + testsuffix) << data << md5sum(data) << url << proxy << auth << proxyauthcount; + + data = QByteArray("abcd\0\1\2\abcd",12); + QTest::newRow("with-nul" + testsuffix) << data << md5sum(data) << url << proxy << auth << proxyauthcount; + + data = QByteArray(4097, '\4'); + QTest::newRow("4k+1" + testsuffix) << data << md5sum(data) << url << proxy << auth << proxyauthcount; + + data = QByteArray(128*1024+1, '\177'); + QTest::newRow("128k+1" + testsuffix) << data << md5sum(data) << url << proxy << auth << proxyauthcount; + } +} + +void tst_QNetworkReply::ioPostToHttpFromSocket() +{ + qRegisterMetaType(); // for QSignalSpy + qRegisterMetaType(); + qRegisterMetaType(); + + QFETCH(QByteArray, data); + QFETCH(QUrl, url); + QFETCH(QNetworkProxy, proxy); + SocketPair socketpair; + socketpair.create(); + QVERIFY(socketpair.endPoints[0] && socketpair.endPoints[1]); + + socketpair.endPoints[0]->write(data); + + QNetworkRequest request(url); + request.setRawHeader("Content-Type", "application/octet-stream"); + + manager.setProxy(proxy); + QNetworkReplyPtr reply = manager.post(request, socketpair.endPoints[1]); + socketpair.endPoints[0]->close(); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + connect(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + connect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), + SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*))); + + QSignalSpy authenticationRequiredSpy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*))); + QSignalSpy proxyAuthenticationRequiredSpy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + + QTestEventLoop::instance().enterLoop(12); + disconnect(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + disconnect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), + this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*))); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::NoError); + // verify that the HTTP status code is 200 Ok + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + + QCOMPARE(reply->readAll().trimmed(), md5sum(data).toHex()); + + QTEST(authenticationRequiredSpy.count(), "authenticationRequiredCount"); + QTEST(proxyAuthenticationRequiredSpy.count(), "proxyAuthenticationRequiredCount"); +} + +void tst_QNetworkReply::ioPostToHttpFromSocketSynchronous_data() +{ + QTest::addColumn("data"); + QTest::addColumn("md5sum"); + + QByteArray data; + data = ""; + QTest::newRow("empty") << data << md5sum(data); + + data = "This is a normal message."; + QTest::newRow("generic") << data << md5sum(data); + + data = "This is a message to show that Qt rocks!\r\n\n"; + QTest::newRow("small") << data << md5sum(data); + + data = QByteArray("abcd\0\1\2\abcd",12); + QTest::newRow("with-nul") << data << md5sum(data); + + data = QByteArray(4097, '\4'); + QTest::newRow("4k+1") << data << md5sum(data); + + data = QByteArray(128*1024+1, '\177'); + QTest::newRow("128k+1") << data << md5sum(data); + + data = QByteArray(2*1024*1024+1, '\177'); + QTest::newRow("2MB+1") << data << md5sum(data); +} + +void tst_QNetworkReply::ioPostToHttpFromSocketSynchronous() +{ + QFETCH(QByteArray, data); + + SocketPair socketpair; + QVERIFY(socketpair.create()); + QVERIFY(socketpair.endPoints[0] && socketpair.endPoints[1]); + socketpair.endPoints[0]->write(data); + socketpair.endPoints[0]->waitForBytesWritten(5000); + // ### for 4.8: make the socket pair unbuffered, to not read everything in one go in QNetworkReplyImplPrivate::setup() + QTestEventLoop::instance().enterLoop(3); + + QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/md5sum.cgi"); + QNetworkRequest request(url); + request.setRawHeader("Content-Type", "application/octet-stream"); + request.setAttribute( + QNetworkRequest::SynchronousRequestAttribute, + true); + + QNetworkReplyPtr reply = manager.post(request, socketpair.endPoints[1]); + QVERIFY(reply->isFinished()); + socketpair.endPoints[0]->close(); + + QCOMPARE(reply->error(), QNetworkReply::NoError); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::NoError); + // verify that the HTTP status code is 200 Ok + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + + QCOMPARE(reply->readAll().trimmed(), md5sum(data).toHex()); +} + +// this tests checks if rewinding the POST-data to some place in the middle +// worked. +void tst_QNetworkReply::ioPostToHttpFromMiddleOfFileToEnd() +{ + QFile sourceFile(SRCDIR "/rfc3252.txt"); + QVERIFY(sourceFile.open(QIODevice::ReadOnly)); + // seeking to the middle + sourceFile.seek(sourceFile.size() / 2); + + QUrl url = "http://" + QtNetworkSettings::serverName() + "/qtest/protected/cgi-bin/md5sum.cgi"; + QNetworkRequest request(url); + request.setRawHeader("Content-Type", "application/octet-stream"); + QNetworkReplyPtr reply = manager.post(request, &sourceFile); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + connect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), + SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*))); + + QTestEventLoop::instance().enterLoop(2); + disconnect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), + this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*))); + QVERIFY(!QTestEventLoop::instance().timeout()); + + // compare half data + sourceFile.seek(sourceFile.size() / 2); + QByteArray data = sourceFile.readAll(); + QCOMPARE(reply->readAll().trimmed(), md5sum(data).toHex()); +} + +void tst_QNetworkReply::ioPostToHttpFromMiddleOfFileFiveBytes() +{ + QFile sourceFile(SRCDIR "/rfc3252.txt"); + QVERIFY(sourceFile.open(QIODevice::ReadOnly)); + // seeking to the middle + sourceFile.seek(sourceFile.size() / 2); + + QUrl url = "http://" + QtNetworkSettings::serverName() + "/qtest/protected/cgi-bin/md5sum.cgi"; + QNetworkRequest request(url); + request.setRawHeader("Content-Type", "application/octet-stream"); + // only send 5 bytes + request.setHeader(QNetworkRequest::ContentLengthHeader, 5); + QVERIFY(request.header(QNetworkRequest::ContentLengthHeader).isValid()); + QNetworkReplyPtr reply = manager.post(request, &sourceFile); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + connect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), + SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*))); + + QTestEventLoop::instance().enterLoop(2); + disconnect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), + this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*))); + QVERIFY(!QTestEventLoop::instance().timeout()); + + // compare half data + sourceFile.seek(sourceFile.size() / 2); + QByteArray data = sourceFile.read(5); + QCOMPARE(reply->readAll().trimmed(), md5sum(data).toHex()); +} + +void tst_QNetworkReply::ioPostToHttpFromMiddleOfQBufferFiveBytes() +{ + // test needed since a QBuffer goes with a different codepath than the QFile + // tested in ioPostToHttpFromMiddleOfFileFiveBytes + QBuffer uploadBuffer; + uploadBuffer.open(QIODevice::ReadWrite); + uploadBuffer.write("1234567890"); + uploadBuffer.seek(5); + + QUrl url = "http://" + QtNetworkSettings::serverName() + "/qtest/protected/cgi-bin/md5sum.cgi"; + QNetworkRequest request(url); + request.setRawHeader("Content-Type", "application/octet-stream"); + QNetworkReplyPtr reply = manager.post(request, &uploadBuffer); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + connect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), + SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*))); + + QTestEventLoop::instance().enterLoop(2); + disconnect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), + this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*))); + QVERIFY(!QTestEventLoop::instance().timeout()); + + // compare half data + uploadBuffer.seek(5); + QByteArray data = uploadBuffer.read(5); + QCOMPARE(reply->readAll().trimmed(), md5sum(data).toHex()); +} + + +void tst_QNetworkReply::ioPostToHttpNoBufferFlag() +{ + QByteArray data = QByteArray("daaaaaaataaaaaaa"); + // create a sequential QIODevice by feeding the data into a local TCP server + SocketPair socketpair; + socketpair.create(); + QVERIFY(socketpair.endPoints[0] && socketpair.endPoints[1]); + socketpair.endPoints[0]->write(data); + + QUrl url = "http://" + QtNetworkSettings::serverName() + "/qtest/protected/cgi-bin/md5sum.cgi"; + QNetworkRequest request(url); + request.setRawHeader("Content-Type", "application/octet-stream"); + // disallow buffering + request.setAttribute(QNetworkRequest::DoNotBufferUploadDataAttribute, true); + request.setHeader(QNetworkRequest::ContentLengthHeader, data.size()); + QNetworkReplyPtr reply = manager.post(request, socketpair.endPoints[1]); + socketpair.endPoints[0]->close(); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + connect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), + SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*))); + + QTestEventLoop::instance().enterLoop(2); + disconnect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), + this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*))); + + // verify: error code is QNetworkReply::ContentReSendError + QCOMPARE(reply->error(), QNetworkReply::ContentReSendError); +} + +#ifndef QT_NO_OPENSSL +class SslServer : public QTcpServer { + Q_OBJECT +public: + SslServer() : socket(0) {}; + void incomingConnection(int socketDescriptor) { + QSslSocket *serverSocket = new QSslSocket; + serverSocket->setParent(this); + + if (serverSocket->setSocketDescriptor(socketDescriptor)) { + connect(serverSocket, SIGNAL(encrypted()), this, SLOT(encryptedSlot())); + connect(serverSocket, SIGNAL(readyRead()), this, SLOT(readyReadSlot())); + serverSocket->setProtocol(QSsl::AnyProtocol); + connect(serverSocket, SIGNAL(sslErrors(const QList&)), serverSocket, SLOT(ignoreSslErrors())); + serverSocket->setLocalCertificate(SRCDIR "/certs/server.pem"); + serverSocket->setPrivateKey(SRCDIR "/certs/server.key"); + serverSocket->startServerEncryption(); + } else { + delete serverSocket; + } + } +signals: + void newEncryptedConnection(); +public slots: + void encryptedSlot() { + socket = (QSslSocket*) sender(); + emit newEncryptedConnection(); + } + void readyReadSlot() { + // for the incoming sockets, not the server socket + //qDebug() << static_cast(sender())->bytesAvailable() << static_cast(sender())->encryptedBytesAvailable(); + } + +public: + QSslSocket *socket; +}; + +// very similar to ioPostToHttpUploadProgress but for SSL +void tst_QNetworkReply::ioPostToHttpsUploadProgress() +{ + //QFile sourceFile(SRCDIR "/bigfile"); + //QVERIFY(sourceFile.open(QIODevice::ReadOnly)); + qint64 wantedSize = 2*1024*1024; // 2 MB + QByteArray sourceFile; + // And in the case of SSL, the compression can fool us and let the + // server send the data much faster than expected. + // So better provide random data that cannot be compressed. + for (int i = 0; i < wantedSize; ++i) + sourceFile += (char)qrand(); + + // emulate a minimal https server + SslServer server; + server.listen(QHostAddress(QHostAddress::LocalHost), 0); + + // create the request + QUrl url = QUrl(QString("https://127.0.0.1:%1/").arg(server.serverPort())); + QNetworkRequest request(url); + + request.setRawHeader("Content-Type", "application/octet-stream"); + QNetworkReplyPtr reply = manager.post(request, sourceFile); + + QSignalSpy spy(reply, SIGNAL(uploadProgress(qint64,qint64))); + connect(&server, SIGNAL(newEncryptedConnection()), &QTestEventLoop::instance(), SLOT(exitLoop())); + connect(reply, SIGNAL(sslErrors(const QList&)), reply, SLOT(ignoreSslErrors())); + + // get the request started and the incoming socket connected + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + QTcpSocket *incomingSocket = server.socket; + QVERIFY(incomingSocket); + disconnect(&server, SIGNAL(newEncryptedConnection()), &QTestEventLoop::instance(), SLOT(exitLoop())); + + + incomingSocket->setReadBufferSize(1*1024); + QTestEventLoop::instance().enterLoop(2); + // some progress should have been made + QVERIFY(!spy.isEmpty()); + QList args = spy.last(); + QVERIFY(args.at(0).toLongLong() > 0); + // but not everything! + QVERIFY(args.at(0).toLongLong() != sourceFile.size()); + + // set the read buffer to unlimited + incomingSocket->setReadBufferSize(0); + QTestEventLoop::instance().enterLoop(10); + // progress should be finished + QVERIFY(!spy.isEmpty()); + QList args3 = spy.last(); + QCOMPARE(args3.at(0).toLongLong(), args3.at(1).toLongLong()); + QCOMPARE(args3.at(0).toLongLong(), qint64(sourceFile.size())); + + // after sending this, the QNAM should emit finished() + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + incomingSocket->write("HTTP/1.0 200 OK\r\n"); + incomingSocket->write("Content-Length: 0\r\n"); + incomingSocket->write("\r\n"); + QTestEventLoop::instance().enterLoop(10); + // not timeouted -> finished() was emitted + QVERIFY(!QTestEventLoop::instance().timeout()); + + incomingSocket->close(); + server.close(); +} +#endif + +void tst_QNetworkReply::ioGetFromBuiltinHttp_data() +{ + QTest::addColumn("https"); + QTest::addColumn("bufferSize"); + QTest::newRow("http+unlimited") << false << 0; + QTest::newRow("http+limited") << false << 4096; +#ifndef QT_NO_OPENSSL + QTest::newRow("https+unlimited") << true << 0; + QTest::newRow("https+limited") << true << 4096; +#endif +} + +void tst_QNetworkReply::ioGetFromBuiltinHttp() +{ + QSKIP("Limiting is broken right now, check QTBUG-15065", SkipAll); + QFETCH(bool, https); + QFETCH(int, bufferSize); + + QByteArray testData; + // Make the data big enough so that it can fill the kernel buffer + // (which seems to hold 202 KB here) + const int wantedSize = 1200 * 1000; + testData.reserve(wantedSize); + // And in the case of SSL, the compression can fool us and let the + // server send the data much faster than expected. + // So better provide random data that cannot be compressed. + for (int i = 0; i < wantedSize; ++i) + testData += (char)qrand(); + + QByteArray httpResponse = QByteArray("HTTP/1.0 200 OK\r\nContent-Length: "); + httpResponse += QByteArray::number(testData.size()); + httpResponse += "\r\n\r\n"; + httpResponse += testData; + + qDebug() << "Server will send" << (httpResponse.size()-testData.size()) << "bytes of header and" + << testData.size() << "bytes of data"; + + const bool fillKernelBuffer = bufferSize > 0; + FastSender server(httpResponse, https, fillKernelBuffer); + + QUrl url(QString("%1://127.0.0.1:%2/qtest/rfc3252.txt") + .arg(https?"https":"http") + .arg(server.serverPort())); + QNetworkRequest request(url); + QNetworkReplyPtr reply = manager.get(request); + reply->setReadBufferSize(bufferSize); + reply->ignoreSslErrors(); + const int rate = 200; // in kB per sec + RateControlledReader reader(server, reply, rate, bufferSize); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTime loopTime; + loopTime.start(); + QTestEventLoop::instance().enterLoop(30); + const int elapsedTime = loopTime.elapsed(); + server.wait(); + reader.wrapUp(); + + qDebug() << "send rate:" << server.transferRate << "B/s"; + qDebug() << "receive rate:" << reader.totalBytesRead * 1000 / elapsedTime + << "(it received" << reader.totalBytesRead << "bytes in" << elapsedTime << "ms)"; + + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(reply->url(), request.url()); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + + QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), (qint64)testData.size()); + if (reader.data.size() < testData.size()) { // oops? + QCOMPARE(reader.data, testData.mid(0, reader.data.size())); + qDebug() << "The data is incomplete, the last" << testData.size() - reader.data.size() << "bytes are missing"; + QEXPECT_FAIL("http+limited", "Limiting is broken right now, check QTBUG-15065", Abort); + QEXPECT_FAIL("https+limited", "Limiting is broken right now, check QTBUG-15065", Abort); + } + QCOMPARE(reader.data.size(), testData.size()); + QCOMPARE(reader.data, testData); + + // OK we got the file alright, but did setReadBufferSize work? + QVERIFY(server.transferRate != -1); + if (bufferSize > 0) { + const int allowedDeviation = 16; // TODO find out why the send rate is 13% faster currently + const int minRate = rate * 1024 * (100-allowedDeviation) / 100; + const int maxRate = rate * 1024 * (100+allowedDeviation) / 100; + qDebug() << minRate << "<="<< server.transferRate << "<=" << maxRate << "?"; + QEXPECT_FAIL("http+limited", "Limiting is broken right now, check QTBUG-15065", Continue); + QEXPECT_FAIL("https+limited", "Limiting is broken right now, check QTBUG-15065", Continue); + QVERIFY(server.transferRate >= minRate && server.transferRate <= maxRate); + } +} + +void tst_QNetworkReply::ioPostToHttpUploadProgress() +{ + QFile sourceFile(SRCDIR "/bigfile"); + QVERIFY(sourceFile.open(QIODevice::ReadOnly)); + + // emulate a minimal http server + QTcpServer server; + server.listen(QHostAddress(QHostAddress::LocalHost), 0); + + // create the request + QUrl url = QUrl(QString("http://127.0.0.1:%1/").arg(server.serverPort())); + QNetworkRequest request(url); + request.setRawHeader("Content-Type", "application/octet-stream"); + QNetworkReplyPtr reply = manager.post(request, &sourceFile); + QSignalSpy spy(reply, SIGNAL(uploadProgress(qint64,qint64))); + connect(&server, SIGNAL(newConnection()), &QTestEventLoop::instance(), SLOT(exitLoop())); + + // get the request started and the incoming socket connected + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + QTcpSocket *incomingSocket = server.nextPendingConnection(); + QVERIFY(incomingSocket); + disconnect(&server, SIGNAL(newConnection()), &QTestEventLoop::instance(), SLOT(exitLoop())); + + incomingSocket->setReadBufferSize(1*1024); + QTestEventLoop::instance().enterLoop(5); + // some progress should have been made + QList args = spy.last(); + QVERIFY(!args.isEmpty()); + QVERIFY(args.at(0).toLongLong() > 0); + // but not everything! + QVERIFY(args.at(0).toLongLong() != sourceFile.size()); + + // set the read buffer to unlimited + incomingSocket->setReadBufferSize(0); + QTestEventLoop::instance().enterLoop(10); + // progress should be finished + QList args3 = spy.last(); + QVERIFY(!args3.isEmpty()); + // More progress than before + QVERIFY(args3.at(0).toLongLong() > args.at(0).toLongLong()); + QCOMPARE(args3.at(0).toLongLong(), args3.at(1).toLongLong()); + // And actually finished.. + QCOMPARE(args3.at(0).toLongLong(), sourceFile.size()); + + // after sending this, the QNAM should emit finished() + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + incomingSocket->write("HTTP/1.0 200 OK\r\n"); + incomingSocket->write("Content-Length: 0\r\n"); + incomingSocket->write("\r\n"); + QTestEventLoop::instance().enterLoop(10); + // not timeouted -> finished() was emitted + QVERIFY(!QTestEventLoop::instance().timeout()); + + incomingSocket->close(); + server.close(); +} + +void tst_QNetworkReply::ioPostToHttpEmptyUploadProgress() +{ + QByteArray ba; + ba.resize(0); + QBuffer buffer(&ba,0); + QVERIFY(buffer.open(QIODevice::ReadOnly)); + + // emulate a minimal http server + QTcpServer server; + server.listen(QHostAddress(QHostAddress::LocalHost), 0); + + // create the request + QUrl url = QUrl(QString("http://127.0.0.1:%1/").arg(server.serverPort())); + QNetworkRequest request(url); + request.setRawHeader("Content-Type", "application/octet-stream"); + QNetworkReplyPtr reply = manager.post(request, &buffer); + QSignalSpy spy(reply, SIGNAL(uploadProgress(qint64,qint64))); + connect(&server, SIGNAL(newConnection()), &QTestEventLoop::instance(), SLOT(exitLoop())); + + + // get the request started and the incoming socket connected + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + QTcpSocket *incomingSocket = server.nextPendingConnection(); + QVERIFY(incomingSocket); + + // after sending this, the QNAM should emit finished() + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + incomingSocket->write("HTTP/1.0 200 OK\r\n"); + incomingSocket->write("Content-Length: 0\r\n"); + incomingSocket->write("\r\n"); + incomingSocket->flush(); + QTestEventLoop::instance().enterLoop(10); + // not timeouted -> finished() was emitted + QVERIFY(!QTestEventLoop::instance().timeout()); + + // final check: only 1 uploadProgress has been emitted + QVERIFY(spy.length() == 1); + QList args = spy.last(); + QVERIFY(!args.isEmpty()); + QCOMPARE(args.at(0).toLongLong(), buffer.size()); + QCOMPARE(args.at(0).toLongLong(), buffer.size()); + + incomingSocket->close(); + server.close(); +} + +void tst_QNetworkReply::lastModifiedHeaderForFile() +{ + QFileInfo fileInfo(SRCDIR "/bigfile"); + QVERIFY(fileInfo.exists()); + + QUrl url = QUrl::fromLocalFile(fileInfo.filePath()); + + QNetworkRequest request(url); + QNetworkReplyPtr reply = manager.head(request); + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QDateTime header = reply->header(QNetworkRequest::LastModifiedHeader).toDateTime(); + QCOMPARE(header, fileInfo.lastModified()); +} + +void tst_QNetworkReply::lastModifiedHeaderForHttp() +{ + // Tue, 22 May 2007 12:04:57 GMT according to webserver + QUrl url = "http://" + QtNetworkSettings::serverName() + "/qtest/fluke.gif"; + + QNetworkRequest request(url); + QNetworkReplyPtr reply = manager.head(request); + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QDateTime header = reply->header(QNetworkRequest::LastModifiedHeader).toDateTime(); + QDateTime realDate = QDateTime::fromString("2007-05-22T12:04:57", Qt::ISODate); + realDate.setTimeSpec(Qt::UTC); + + QCOMPARE(header, realDate); +} + +void tst_QNetworkReply::httpCanReadLine() +{ + QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt")); + QNetworkReplyPtr reply = manager.get(request); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + QCOMPARE(reply->error(), QNetworkReply::NoError); + + QVERIFY(reply->canReadLine()); + QVERIFY(!reply->readAll().isEmpty()); + QVERIFY(!reply->canReadLine()); +} + +void tst_QNetworkReply::rateControl_data() +{ + QTest::addColumn("rate"); + + QTest::newRow("15") << 15; + QTest::newRow("40") << 40; + QTest::newRow("73") << 73; + QTest::newRow("80") << 80; + QTest::newRow("125") << 125; + QTest::newRow("250") << 250; + QTest::newRow("1024") << 1024; +} + +void tst_QNetworkReply::rateControl() +{ + QSKIP("Test disabled -- only for manual purposes", SkipAll); + // this function tests that we aren't reading from the network + // faster than the data is being consumed. + QFETCH(int, rate); + + // ask for 20 seconds worth of data + FastSender sender(20 * rate * 1024); + + QNetworkRequest request("debugpipe://localhost:" + QString::number(sender.serverPort())); + QNetworkReplyPtr reply = manager.get(request); + reply->setReadBufferSize(32768); + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + qRegisterMetaType("QNetworkReply::NetworkError"); + QSignalSpy errorSpy(reply, SIGNAL(error(QNetworkReply::NetworkError))); + + RateControlledReader reader(sender, reply, rate, 20); + + // this test is designed to run for 25 seconds at most + QTime loopTime; + loopTime.start(); + QTestEventLoop::instance().enterLoop(40); + int elapsedTime = loopTime.elapsed(); + + if (!errorSpy.isEmpty()) { + qDebug() << "ERROR!" << errorSpy[0][0] << reply->errorString(); + } + + qDebug() << "tst_QNetworkReply::rateControl" << "send rate:" << sender.transferRate; + qDebug() << "tst_QNetworkReply::rateControl" << "receive rate:" << reader.totalBytesRead * 1000 / elapsedTime + << "(it received" << reader.totalBytesRead << "bytes in" << elapsedTime << "ms)"; + + sender.wait(); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(reply->url(), request.url()); + QCOMPARE(reply->error(), QNetworkReply::NoError); + + QVERIFY(sender.transferRate != -1); + int minRate = rate * 1024 * 9 / 10; + int maxRate = rate * 1024 * 11 / 10; + QVERIFY(sender.transferRate >= minRate); + QVERIFY(sender.transferRate <= maxRate); +} + +void tst_QNetworkReply::downloadProgress_data() +{ + QTest::addColumn("loopCount"); + + QTest::newRow("empty") << 0; + QTest::newRow("small") << 4; +#ifndef Q_OS_SYMBIAN + QTest::newRow("big") << 4096; +#else + // it can run even with 4096 + // but it takes lot time + //especially on emulator + QTest::newRow("big") << 1024; +#endif +} + +void tst_QNetworkReply::downloadProgress() +{ + QTcpServer server; + QVERIFY(server.listen()); + + QNetworkRequest request("debugpipe://127.0.0.1:" + QString::number(server.serverPort()) + "/?bare=1"); + QNetworkReplyPtr reply = manager.get(request); + QSignalSpy spy(reply, SIGNAL(downloadProgress(qint64,qint64))); + connect(reply, SIGNAL(downloadProgress(qint64,qint64)), + &QTestEventLoop::instance(), SLOT(exitLoop())); + QVERIFY(spy.isValid()); + QVERIFY(!reply->isFinished()); + QVERIFY(reply->isRunning()); + + QCoreApplication::instance()->processEvents(); + if (!server.hasPendingConnections()) + server.waitForNewConnection(1000); + QVERIFY(server.hasPendingConnections()); + QCOMPARE(spy.count(), 0); + + QByteArray data(128, 'a'); + QTcpSocket *sender = server.nextPendingConnection(); + QVERIFY(sender); + + QFETCH(int, loopCount); + for (int i = 1; i <= loopCount; ++i) { + sender->write(data); + QVERIFY2(sender->waitForBytesWritten(2000), "Network timeout"); + + spy.clear(); + QTestEventLoop::instance().enterLoop(2); + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(spy.count() > 0); + QVERIFY(!reply->isFinished()); + QVERIFY(reply->isRunning()); + + QList args = spy.last(); + QCOMPARE(args.at(0).toInt(), i*data.size()); + QCOMPARE(args.at(1).toInt(), -1); + } + + // close the connection: + delete sender; + + spy.clear(); + QTestEventLoop::instance().enterLoop(2); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(spy.count() > 0); + QVERIFY(!reply->isRunning()); + QVERIFY(reply->isFinished()); + + QList args = spy.last(); + QCOMPARE(args.at(0).toInt(), loopCount * data.size()); + QCOMPARE(args.at(1).toInt(), loopCount * data.size()); +} + +void tst_QNetworkReply::uploadProgress_data() +{ + putToFile_data(); +} + +void tst_QNetworkReply::uploadProgress() +{ + QFETCH(QByteArray, data); + QTcpServer server; + QVERIFY(server.listen()); + + QNetworkRequest request("debugpipe://127.0.0.1:" + QString::number(server.serverPort()) + "/?bare=1"); + QNetworkReplyPtr reply = manager.put(request, data); + QSignalSpy spy(reply, SIGNAL(uploadProgress(qint64,qint64))); + QSignalSpy finished(reply, SIGNAL(finished())); + QVERIFY(spy.isValid()); + QVERIFY(finished.isValid()); + + QCoreApplication::instance()->processEvents(); + if (!server.hasPendingConnections()) + server.waitForNewConnection(1000); + QVERIFY(server.hasPendingConnections()); + + QTcpSocket *receiver = server.nextPendingConnection(); + if (finished.count() == 0) { + // it's not finished yet, so wait for it to be + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(2); + QVERIFY(!QTestEventLoop::instance().timeout()); + } + delete receiver; + + QVERIFY(finished.count() > 0); + QVERIFY(spy.count() > 0); + + QList args = spy.last(); + QCOMPARE(args.at(0).toInt(), data.size()); + QCOMPARE(args.at(1).toInt(), data.size()); +} + +void tst_QNetworkReply::chaining_data() +{ + putToFile_data(); +} + +void tst_QNetworkReply::chaining() +{ + QTemporaryFile sourceFile(QDir::currentPath() + "/temp-XXXXXX"); + sourceFile.setAutoRemove(true); + QVERIFY(sourceFile.open()); + + QFETCH(QByteArray, data); + QVERIFY(sourceFile.write(data) == data.size()); + sourceFile.flush(); + QCOMPARE(sourceFile.size(), qint64(data.size())); + + QNetworkRequest request(QUrl::fromLocalFile(sourceFile.fileName())); + QNetworkReplyPtr getReply = manager.get(request); + + QFile targetFile(testFileName); + QUrl url = QUrl::fromLocalFile(targetFile.fileName()); + request.setUrl(url); + QNetworkReplyPtr putReply = manager.put(request, getReply); + + connect(putReply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(getReply->url(), QUrl::fromLocalFile(sourceFile.fileName())); + QCOMPARE(getReply->error(), QNetworkReply::NoError); + QCOMPARE(getReply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), sourceFile.size()); + + QCOMPARE(putReply->url(), url); + QCOMPARE(putReply->error(), QNetworkReply::NoError); + QCOMPARE(putReply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0)); + QVERIFY(putReply->readAll().isEmpty()); + + QVERIFY(sourceFile.atEnd()); + sourceFile.seek(0); // reset it to the beginning + + QVERIFY(targetFile.open(QIODevice::ReadOnly)); + QCOMPARE(targetFile.size(), sourceFile.size()); + QCOMPARE(targetFile.readAll(), sourceFile.readAll()); +} + +void tst_QNetworkReply::receiveCookiesFromHttp_data() +{ + QTest::addColumn("cookieString"); + QTest::addColumn >("expectedCookiesFromHttp"); + QTest::addColumn >("expectedCookiesInJar"); + + QTest::newRow("empty") << "" << QList() << QList(); + + QList header, jar; + QNetworkCookie cookie("a", "b"); + header << cookie; + cookie.setDomain(QtNetworkSettings::serverName()); + cookie.setPath("/qtest/cgi-bin/"); + jar << cookie; + QTest::newRow("simple-cookie") << "a=b" << header << jar; + + header << QNetworkCookie("c", "d"); + cookie.setName("c"); + cookie.setValue("d"); + jar << cookie; + QTest::newRow("two-cookies") << "a=b, c=d" << header << jar; + QTest::newRow("two-cookies-2") << "a=b\nc=d" << header << jar; + + header.clear(); + jar.clear(); + cookie = QNetworkCookie("a", "b"); + cookie.setPath("/not/part-of-path"); + header << cookie; + cookie.setDomain(QtNetworkSettings::serverName()); + jar << cookie; + QTest::newRow("invalid-cookie-path") << "a=b; path=/not/part-of-path" << header << jar; + + jar.clear(); + cookie = QNetworkCookie("a", "b"); + cookie.setDomain(".example.com"); + header.clear(); + header << cookie; + QTest::newRow("invalid-cookie-domain") << "a=b; domain=.example.com" << header << jar; +} + +void tst_QNetworkReply::receiveCookiesFromHttp() +{ + QFETCH(QString, cookieString); + + QByteArray data = cookieString.toLatin1() + '\n'; + QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/set-cookie.cgi"); + QNetworkRequest request(url); + request.setRawHeader("Content-Type", "application/octet-stream"); + QNetworkReplyPtr reply; + RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PostOperation, request, reply, data)); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::NoError); + + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok + + QList setCookies = + qvariant_cast >(reply->header(QNetworkRequest::SetCookieHeader)); + QTEST(setCookies, "expectedCookiesFromHttp"); + QTEST(cookieJar->allCookies(), "expectedCookiesInJar"); +} + +void tst_QNetworkReply::receiveCookiesFromHttpSynchronous_data() +{ + tst_QNetworkReply::receiveCookiesFromHttp_data(); +} + +void tst_QNetworkReply::receiveCookiesFromHttpSynchronous() +{ + QFETCH(QString, cookieString); + + QByteArray data = cookieString.toLatin1() + '\n'; + QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/set-cookie.cgi"); + + QNetworkRequest request(url); + request.setRawHeader("Content-Type", "application/octet-stream"); + request.setAttribute( + QNetworkRequest::SynchronousRequestAttribute, + true); + + QNetworkReplyPtr reply; + RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PostOperation, request, reply, data)); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::NoError); + + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok + + QList setCookies = + qvariant_cast >(reply->header(QNetworkRequest::SetCookieHeader)); + QTEST(setCookies, "expectedCookiesFromHttp"); + QTEST(cookieJar->allCookies(), "expectedCookiesInJar"); +} + +void tst_QNetworkReply::sendCookies_data() +{ + QTest::addColumn >("cookiesToSet"); + QTest::addColumn("expectedCookieString"); + + QList list; + QTest::newRow("empty") << list << ""; + + QNetworkCookie cookie("a", "b"); + cookie.setPath("/"); + cookie.setDomain("example.com"); + list << cookie; + QTest::newRow("no-match-domain") << list << ""; + + cookie.setDomain(QtNetworkSettings::serverName()); + cookie.setPath("/something/else"); + list << cookie; + QTest::newRow("no-match-path") << list << ""; + + cookie.setPath("/"); + list << cookie; + QTest::newRow("simple-cookie") << list << "a=b"; + + cookie.setPath("/qtest"); + cookie.setValue("longer"); + list << cookie; + QTest::newRow("two-cookies") << list << "a=longer; a=b"; + + list.clear(); + cookie = QNetworkCookie("a", "b"); + cookie.setPath("/"); + cookie.setDomain("." + QtNetworkSettings::serverDomainName()); + list << cookie; + QTest::newRow("domain-match") << list << "a=b"; + + // but it shouldn't match this: + cookie.setDomain(QtNetworkSettings::serverDomainName()); + list << cookie; + QTest::newRow("domain-match-2") << list << "a=b"; +} + +void tst_QNetworkReply::sendCookies() +{ + QFETCH(QString, expectedCookieString); + QFETCH(QList, cookiesToSet); + cookieJar->setAllCookies(cookiesToSet); + + QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/get-cookie.cgi"); + QNetworkRequest request(url); + QNetworkReplyPtr reply; + RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply)); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::NoError); + + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok + + QCOMPARE(QString::fromLatin1(reply->readAll()).trimmed(), expectedCookieString); +} + +void tst_QNetworkReply::sendCookiesSynchronous_data() +{ + tst_QNetworkReply::sendCookies_data(); +} + +void tst_QNetworkReply::sendCookiesSynchronous() +{ + QFETCH(QString, expectedCookieString); + QFETCH(QList, cookiesToSet); + cookieJar->setAllCookies(cookiesToSet); + + QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/get-cookie.cgi"); + QNetworkRequest request(url); + + request.setAttribute( + QNetworkRequest::SynchronousRequestAttribute, + true); + + QNetworkReplyPtr reply; + RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply)); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::NoError); + + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok + + QCOMPARE(QString::fromLatin1(reply->readAll()).trimmed(), expectedCookieString); +} + +void tst_QNetworkReply::nestedEventLoops_slot() +{ + QEventLoop subloop; + + // 16 seconds: fluke times out in 15 seconds, which triggers a QTcpSocket error + QTimer::singleShot(16000, &subloop, SLOT(quit())); + subloop.exec(); + + QTestEventLoop::instance().exitLoop(); +} + +void tst_QNetworkReply::nestedEventLoops() +{ + // Slightly fragile test, it may not be testing anything + // This is certifying that we're not running into the same issue + // that QHttp had (task 200432): the QTcpSocket connection is + // closed by the remote end because of the kept-alive HTTP + // connection timed out. + // + // The exact time required for this to happen is not exactly + // defined. Our server (Apache httpd) times out after 15 + // seconds. (see above) + + qDebug("Takes 16 seconds to run, please wait"); + qRegisterMetaType(); + + QUrl url("http://" + QtNetworkSettings::serverName()); + QNetworkRequest request(url); + QNetworkReplyPtr reply = manager.get(request); + + QSignalSpy finishedspy(reply, SIGNAL(finished())); + QSignalSpy errorspy(reply, SIGNAL(error(QNetworkReply::NetworkError))); + + connect(reply, SIGNAL(finished()), SLOT(nestedEventLoops_slot())); + QTestEventLoop::instance().enterLoop(20); + QVERIFY2(!QTestEventLoop::instance().timeout(), "Network timeout"); + + QCOMPARE(finishedspy.count(), 1); + QCOMPARE(errorspy.count(), 0); +} + +void tst_QNetworkReply::httpProxyCommands_data() +{ + QTest::addColumn("url"); + QTest::addColumn("responseToSend"); + QTest::addColumn("expectedCommand"); + + QTest::newRow("http") + << QUrl("http://0.0.0.0:4443/http-request") + << QByteArray("HTTP/1.0 200 OK\r\nProxy-Connection: close\r\nContent-Length: 1\r\n\r\n1") + << "GET http://0.0.0.0:4443/http-request HTTP/1."; +#ifndef QT_NO_OPENSSL + QTest::newRow("https") + << QUrl("https://0.0.0.0:4443/https-request") + << QByteArray("HTTP/1.0 200 Connection Established\r\n\r\n") + << "CONNECT 0.0.0.0:4443 HTTP/1."; +#endif +} + +void tst_QNetworkReply::httpProxyCommands() +{ + QFETCH(QUrl, url); + QFETCH(QByteArray, responseToSend); + QFETCH(QString, expectedCommand); + + MiniHttpServer proxyServer(responseToSend); + QNetworkProxy proxy(QNetworkProxy::HttpProxy, "127.0.0.1", proxyServer.serverPort()); + + manager.setProxy(proxy); + QNetworkRequest request(url); + request.setRawHeader("User-Agent", "QNetworkReplyAutoTest/1.0"); + QNetworkReplyPtr reply = manager.get(request); + //clearing the proxy here causes the test to fail. + //the proxy isn't used until after the bearer has been started + //which is correct in general, because system proxy isn't known until that time. + //removing this line is safe, as the proxy is also reset by the cleanup() function + //manager.setProxy(QNetworkProxy()); + + // wait for the finished signal + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + + QTestEventLoop::instance().enterLoop(15); + + QVERIFY(!QTestEventLoop::instance().timeout()); + + //qDebug() << reply->error() << reply->errorString(); + //qDebug() << proxyServer.receivedData; + + // we don't really care if the request succeeded + // especially since it won't succeed in the HTTPS case + // so just check that the command was correct + + QString receivedHeader = proxyServer.receivedData.left(expectedCommand.length()); + QCOMPARE(receivedHeader, expectedCommand); + + //QTBUG-17223 - make sure the user agent from the request is sent to proxy server even for CONNECT + int uapos = proxyServer.receivedData.indexOf("User-Agent"); + int uaend = proxyServer.receivedData.indexOf("\r\n", uapos); + QByteArray uaheader = proxyServer.receivedData.mid(uapos, uaend - uapos); + QCOMPARE(uaheader, QByteArray("User-Agent: QNetworkReplyAutoTest/1.0")); +} + +class ProxyChangeHelper : public QObject { + Q_OBJECT +public: + ProxyChangeHelper() : QObject(), signalCount(0) {}; +public slots: + void finishedSlot() { + signalCount++; + if (signalCount == 2) + QMetaObject::invokeMethod(&QTestEventLoop::instance(), "exitLoop", Qt::QueuedConnection); + } +private: + int signalCount; +}; + +void tst_QNetworkReply::httpProxyCommandsSynchronous_data() +{ + httpProxyCommands_data(); +} + +struct QThreadCleanup +{ + static inline void cleanup(QThread *thread) + { + thread->quit(); + if (thread->wait(3000)) + delete thread; + else + qWarning("thread hung, leaking memory so test can finish"); + } +}; + +struct QDeleteLaterCleanup +{ + static inline void cleanup(QObject *o) + { + o->deleteLater(); + } +}; + +void tst_QNetworkReply::httpProxyCommandsSynchronous() +{ + QFETCH(QUrl, url); + QFETCH(QByteArray, responseToSend); + QFETCH(QString, expectedCommand); + + // when using synchronous commands, we need a different event loop for + // the server thread, because the client is never returning to the + // event loop + QScopedPointer serverThread(new QThread); + QScopedPointer proxyServer(new MiniHttpServer(responseToSend, false, serverThread.data())); + QNetworkProxy proxy(QNetworkProxy::HttpProxy, "127.0.0.1", proxyServer->serverPort()); + + manager.setProxy(proxy); + QNetworkRequest request(url); + + // send synchronous request + request.setAttribute( + QNetworkRequest::SynchronousRequestAttribute, + true); + + QNetworkReplyPtr reply = manager.get(request); + QVERIFY(reply->isFinished()); // synchronous + manager.setProxy(QNetworkProxy()); + + //qDebug() << reply->error() << reply->errorString(); + + // we don't really care if the request succeeded + // especially since it won't succeed in the HTTPS case + // so just check that the command was correct + + QString receivedHeader = proxyServer->receivedData.left(expectedCommand.length()); + QCOMPARE(receivedHeader, expectedCommand); +} + +void tst_QNetworkReply::proxyChange() +{ + ProxyChangeHelper helper; + MiniHttpServer proxyServer( + "HTTP/1.0 200 OK\r\nProxy-Connection: keep-alive\r\n" + "Content-Length: 1\r\n\r\n1"); + QNetworkProxy dummyProxy(QNetworkProxy::HttpProxy, "127.0.0.1", proxyServer.serverPort()); + QNetworkRequest req(QUrl("http://" + QtNetworkSettings::serverName())); + proxyServer.doClose = false; + + manager.setProxy(dummyProxy); + QNetworkReplyPtr reply1 = manager.get(req); + connect(reply1, SIGNAL(finished()), &helper, SLOT(finishedSlot())); + + manager.setProxy(QNetworkProxy()); + QNetworkReplyPtr reply2 = manager.get(req); + connect(reply2, SIGNAL(finished()), &helper, SLOT(finishedSlot())); + + QTestEventLoop::instance().enterLoop(20); + QVERIFY(!QTestEventLoop::instance().timeout()); + + // verify that the replies succeeded + QCOMPARE(reply1->error(), QNetworkReply::NoError); + QCOMPARE(reply1->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + QVERIFY(reply1->size() == 1); + + QCOMPARE(reply2->error(), QNetworkReply::NoError); + QCOMPARE(reply2->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + QVERIFY(reply2->size() > 1); + + // now try again and get an error + // this verifies that we reuse the already-open connection + + proxyServer.doClose = true; + proxyServer.dataToTransmit = + "HTTP/1.0 403 Forbidden\r\nProxy-Connection: close\r\n" + "Content-Length: 1\r\n\r\n1"; + + manager.setProxy(dummyProxy); + QNetworkReplyPtr reply3 = manager.get(req); + connect(reply3, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(5); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QVERIFY(int(reply3->error()) > 0); +} + +void tst_QNetworkReply::authorizationError_data() +{ + + QTest::addColumn("url"); + QTest::addColumn("errorSignalCount"); + QTest::addColumn("finishedSignalCount"); + QTest::addColumn("error"); + QTest::addColumn("httpStatusCode"); + QTest::addColumn("httpBody"); + + QTest::newRow("unknown-authorization-method") << "http://" + QtNetworkSettings::serverName() + + "/qtest/cgi-bin/http-unknown-authentication-method.cgi?401-authorization-required" << 1 << 1 + << int(QNetworkReply::AuthenticationRequiredError) << 401 << "authorization required"; + QTest::newRow("unknown-proxy-authorization-method") << "http://" + QtNetworkSettings::serverName() + + "/qtest/cgi-bin/http-unknown-authentication-method.cgi?407-proxy-authorization-required" << 1 << 1 + << int(QNetworkReply::ProxyAuthenticationRequiredError) << 407 + << "authorization required"; +} + +void tst_QNetworkReply::authorizationError() +{ + QFETCH(QString, url); + QNetworkRequest request(url); + QNetworkReplyPtr reply = manager.get(request); + + QCOMPARE(reply->error(), QNetworkReply::NoError); + + qRegisterMetaType("QNetworkReply::NetworkError"); + QSignalSpy errorSpy(reply, SIGNAL(error(QNetworkReply::NetworkError))); + QSignalSpy finishedSpy(reply, SIGNAL(finished())); + // now run the request: + connect(reply, SIGNAL(finished()), + &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QFETCH(int, errorSignalCount); + QCOMPARE(errorSpy.count(), errorSignalCount); + QFETCH(int, finishedSignalCount); + QCOMPARE(finishedSpy.count(), finishedSignalCount); + QFETCH(int, error); + QCOMPARE(reply->error(), QNetworkReply::NetworkError(error)); + + QFETCH(int, httpStatusCode); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), httpStatusCode); + + QFETCH(QString, httpBody); + QCOMPARE(qint64(reply->size()), qint64(httpBody.size())); + QCOMPARE(QString(reply->readAll()), httpBody); +} + +void tst_QNetworkReply::httpConnectionCount() +{ + QTcpServer server; + QVERIFY(server.listen()); + QCoreApplication::instance()->processEvents(); + + for (int i = 0; i < 10; i++) { + QNetworkRequest request (QUrl("http://127.0.0.1:" + QString::number(server.serverPort()) + "/" + QString::number(i))); + QNetworkReply* reply = manager.get(request); + reply->setParent(&server); + } + + int pendingConnectionCount = 0; + QTime time; + time.start(); + + while(pendingConnectionCount <= 20) { + QTestEventLoop::instance().enterLoop(1); + QTcpSocket *socket = server.nextPendingConnection(); + while (socket != 0) { + pendingConnectionCount++; + socket->setParent(&server); + socket = server.nextPendingConnection(); + } + + // at max. wait 10 sec + if (time.elapsed() > 10000) + break; + } + +#ifdef Q_OS_SYMBIAN + // see in qhttpnetworkconnection.cpp + // hardcoded defaultChannelCount = 3 + QCOMPARE(pendingConnectionCount, 3); +#else + QCOMPARE(pendingConnectionCount, 6); +#endif +} + +void tst_QNetworkReply::httpReUsingConnectionSequential_data() +{ + QTest::addColumn("doDeleteLater"); + QTest::newRow("deleteLater") << true; + QTest::newRow("noDeleteLater") << false; +} + +void tst_QNetworkReply::httpReUsingConnectionSequential() +{ + QFETCH(bool, doDeleteLater); + + QByteArray response("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + MiniHttpServer server(response); + server.multiple = true; + server.doClose = false; + + QUrl url; + url.setScheme("http"); + url.setPort(server.serverPort()); + url.setHost("127.0.0.1"); + // first request + QNetworkReply* reply1 = manager.get(QNetworkRequest(url)); + connect(reply1, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(2); + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(!reply1->error()); + int reply1port = server.client->peerPort(); + + if (doDeleteLater) + reply1->deleteLater(); + + // finished received, send the next one + QNetworkReply*reply2 = manager.get(QNetworkRequest(url)); + connect(reply2, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(2); + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(!reply2->error()); + int reply2port = server.client->peerPort(); // should still be the same object + + QVERIFY(reply1port > 0); + QCOMPARE(server.totalConnections, 1); + QCOMPARE(reply2port, reply1port); + + if (!doDeleteLater) + reply1->deleteLater(); // only do it if it was not done earlier + reply2->deleteLater(); +} + +class HttpReUsingConnectionFromFinishedSlot : public QObject { + Q_OBJECT +public: + QNetworkReply* reply1; + QNetworkReply* reply2; + QUrl url; + QNetworkAccessManager manager; +public slots: + void finishedSlot() { + QVERIFY(!reply1->error()); + + QFETCH(bool, doDeleteLater); + if (doDeleteLater) { + reply1->deleteLater(); + reply1 = 0; + } + + // kick off 2nd request and exit the loop when it is done + reply2 = manager.get(QNetworkRequest(url)); + reply2->setParent(this); + connect(reply2, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + } +}; + +void tst_QNetworkReply::httpReUsingConnectionFromFinishedSlot_data() +{ + httpReUsingConnectionSequential_data(); +} + +void tst_QNetworkReply::httpReUsingConnectionFromFinishedSlot() +{ + QByteArray response("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + MiniHttpServer server(response); + server.multiple = true; + server.doClose = false; + + HttpReUsingConnectionFromFinishedSlot helper; + helper.reply1 = 0; + helper.reply2 = 0; + helper.url.setScheme("http"); + helper.url.setPort(server.serverPort()); + helper.url.setHost("127.0.0.1"); + + // first request + helper.reply1 = helper.manager.get(QNetworkRequest(helper.url)); + helper.reply1->setParent(&helper); + connect(helper.reply1, SIGNAL(finished()), &helper, SLOT(finishedSlot())); + QTestEventLoop::instance().enterLoop(4); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QVERIFY(helper.reply2); + QVERIFY(!helper.reply2->error()); + + QCOMPARE(server.totalConnections, 1); +} + +class HttpRecursiveCreationHelper : public QObject { + Q_OBJECT +public: + + HttpRecursiveCreationHelper(): + QObject(0), + requestsStartedCount_finished(0), + requestsStartedCount_readyRead(0), + requestsFinishedCount(0) + { + } + QNetworkAccessManager manager; + int requestsStartedCount_finished; + int requestsStartedCount_readyRead; + int requestsFinishedCount; +public slots: + void finishedSlot() { + requestsFinishedCount++; + + QNetworkReply *reply = qobject_cast(sender()); + QVERIFY(!reply->error()); + QVERIFY(reply->bytesAvailable() == 27906); + + if (requestsFinishedCount == 60) { + QTestEventLoop::instance().exitLoop(); + return; + } + + if (requestsStartedCount_finished < 30) { + startOne(); + requestsStartedCount_finished++; + } + + reply->deleteLater(); + } + void readyReadSlot() { + QNetworkReply *reply = qobject_cast(sender()); + QVERIFY(!reply->error()); + + if (requestsStartedCount_readyRead < 30 && reply->bytesAvailable() > 27906/2) { + startOne(); + requestsStartedCount_readyRead++; + } + } + void startOne() { + QUrl url = "http://" + QtNetworkSettings::serverName() + "/qtest/fluke.gif"; + QNetworkRequest request(url); + QNetworkReply *reply = manager.get(request); + reply->setParent(this); + connect(reply, SIGNAL(finished()), this, SLOT(finishedSlot())); + connect(reply, SIGNAL(readyRead()), this, SLOT(readyReadSlot())); + } +}; + +void tst_QNetworkReply::httpRecursiveCreation() +{ + // this test checks if creation of new requests to the same host properly works + // from readyRead() and finished() signals + HttpRecursiveCreationHelper helper; + helper.startOne(); + QTestEventLoop::instance().enterLoop(30); + QVERIFY(!QTestEventLoop::instance().timeout()); +} + +#ifndef QT_NO_OPENSSL +void tst_QNetworkReply::ignoreSslErrorsList_data() +{ + QTest::addColumn("url"); + QTest::addColumn >("expectedSslErrors"); + QTest::addColumn("expectedNetworkError"); + + QList expectedSslErrors; + // apparently, because of some weird behaviour of SRCDIR, the file name below needs to start with a slash + QList certs = QSslCertificate::fromPath(QLatin1String(SRCDIR "/certs/qt-test-server-cacert.pem")); + QSslError rightError(QSslError::SelfSignedCertificate, certs.at(0)); + QSslError wrongError(QSslError::SelfSignedCertificate); + + QTest::newRow("SSL-failure-empty-list") << "https://" + QtNetworkSettings::serverName() + "/index.html" << expectedSslErrors << QNetworkReply::SslHandshakeFailedError; + expectedSslErrors.append(wrongError); + QTest::newRow("SSL-failure-wrong-error") << "https://" + QtNetworkSettings::serverName() + "/index.html" << expectedSslErrors << QNetworkReply::SslHandshakeFailedError; + expectedSslErrors.append(rightError); + QTest::newRow("allErrorsInExpectedList1") << "https://" + QtNetworkSettings::serverName() + "/index.html" << expectedSslErrors << QNetworkReply::NoError; + expectedSslErrors.removeAll(wrongError); + QTest::newRow("allErrorsInExpectedList2") << "https://" + QtNetworkSettings::serverName() + "/index.html" << expectedSslErrors << QNetworkReply::NoError; + expectedSslErrors.removeAll(rightError); + QTest::newRow("SSL-failure-empty-list-again") << "https://" + QtNetworkSettings::serverName() + "/index.html" << expectedSslErrors << QNetworkReply::SslHandshakeFailedError; +} + +void tst_QNetworkReply::ignoreSslErrorsList() +{ + QFETCH(QString, url); + QNetworkRequest request(url); + QNetworkReplyPtr reply = manager.get(request); + + QFETCH(QList, expectedSslErrors); + reply->ignoreSslErrors(expectedSslErrors); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QFETCH(QNetworkReply::NetworkError, expectedNetworkError); + QCOMPARE(reply->error(), expectedNetworkError); +} + +void tst_QNetworkReply::ignoreSslErrorsListWithSlot_data() +{ + ignoreSslErrorsList_data(); +} + +// this is not a test, just a slot called in the test below +void tst_QNetworkReply::ignoreSslErrorListSlot(QNetworkReply *reply, const QList &) +{ + reply->ignoreSslErrors(storedExpectedSslErrors); +} + +// do the same as in ignoreSslErrorsList, but ignore the errors in the slot +void tst_QNetworkReply::ignoreSslErrorsListWithSlot() +{ + QFETCH(QString, url); + QNetworkRequest request(url); + QNetworkReplyPtr reply = manager.get(request); + + QFETCH(QList, expectedSslErrors); + // store the errors to ignore them later in the slot connected below + storedExpectedSslErrors = expectedSslErrors; + connect(&manager, SIGNAL(sslErrors(QNetworkReply *, const QList &)), + this, SLOT(ignoreSslErrorListSlot(QNetworkReply *, const QList &))); + + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QFETCH(QNetworkReply::NetworkError, expectedNetworkError); + QCOMPARE(reply->error(), expectedNetworkError); +} + +void tst_QNetworkReply::sslConfiguration_data() +{ + QTest::addColumn("configuration"); + QTest::addColumn("works"); + + QTest::newRow("empty") << QSslConfiguration() << false; + QSslConfiguration conf = QSslConfiguration::defaultConfiguration(); + QTest::newRow("default") << conf << false; // does not contain test server cert + QList testServerCert = QSslCertificate::fromPath(SRCDIR "/certs/qt-test-server-cacert.pem"); + conf.setCaCertificates(testServerCert); + QTest::newRow("set-root-cert") << conf << true; + conf.setProtocol(QSsl::SecureProtocols); + QTest::newRow("secure") << conf << true; +} + +void tst_QNetworkReply::sslConfiguration() +{ + QNetworkRequest request(QUrl("https://" + QtNetworkSettings::serverName() + "/index.html")); + QFETCH(QSslConfiguration, configuration); + request.setSslConfiguration(configuration); + QNetworkReplyPtr reply = manager.get(request); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QFETCH(bool, works); + QNetworkReply::NetworkError expectedError = works ? QNetworkReply::NoError : QNetworkReply::SslHandshakeFailedError; + QCOMPARE(reply->error(), expectedError); +} + +#endif // QT_NO_OPENSSL + +void tst_QNetworkReply::getAndThenDeleteObject_data() +{ + QTest::addColumn("replyFirst"); + + QTest::newRow("delete-reply-first") << true; + QTest::newRow("delete-qnam-first") << false; +} + +void tst_QNetworkReply::getAndThenDeleteObject() +{ + // yes, this will leak if the testcase fails. I don't care. It must not fail then :P + QNetworkAccessManager *manager = new QNetworkAccessManager(); + QNetworkRequest request("http://" + QtNetworkSettings::serverName() + "/qtest/bigfile"); + QNetworkReply *reply = manager->get(request); + reply->setReadBufferSize(1); + reply->setParent((QObject*)0); // must be 0 because else it is the manager + + QTime stopWatch; + stopWatch.start(); + forever { + QCoreApplication::instance()->processEvents(); + if (reply->bytesAvailable()) + break; + if (stopWatch.elapsed() >= 30000) + break; + } + + QVERIFY(reply->bytesAvailable()); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + QVERIFY(!reply->isFinished()); // must not be finished + + QFETCH(bool, replyFirst); + + if (replyFirst) { + delete reply; + delete manager; + } else { + delete manager; + delete reply; + } +} + +// see https://bugs.webkit.org/show_bug.cgi?id=38935 +void tst_QNetworkReply::symbianOpenCDataUrlCrash() +{ + QString requestUrl("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAWCAYAAAA1vze2AAAAB3RJTUUH2AUSEgolrgBvVQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAARnQU1BAACxjwv8YQUAAAHlSURBVHja5VbNShxBEK6ZaXtnHTebQPA1gngNmfaeq+QNPIlIXkC9iQdJxJNvEHLN3VkxhxxE8gTmEhAVddXZ6Z3f9Ndriz89/sHmkBQUVVT1fB9d9c3uOERUKTunIdn3HzstxGpYBDS4wZk7TAJj/wlJ90J+jnuygqs8svSj+/rGHBos3rE18XBvfU3no7NzlJfUaY/5whAwl8Lr/WDUv4ODxTMb+P5xLExe5LmO559WqTX/MQR4WZYEAtSePS4pE0qSnuhnRUcBU5Gm2k9XljU4Z26I3NRxBrd80rj2fh+KNE0FY4xevRgTjREvPFpasAK8Xli6MUbbuKw3afAGgSBXozo5u4hkmncAlkl5wx8iMGbdyQjnCFEiEwGiosj1UQA/x2rVddiVoi+l4IxE0PTDnx+mrQBvvnx9cFz3krhVvuhzFn579/aq/n5rW8fbtTqiWhIQZEo17YBvbkxOXNVndnYpTvod7AtiuN2re0+siwcB9oH8VxxrNwQQAhzyRs30n7wTI2HIN2g2QtQwjjhJIQatOq7E8bIVCLwzpl83Lvtvl+NohWWlE8UZTWEMAGCcR77fHKhPnZF5tYie6dfdxCphACmLPM+j8bYfmTryg64kV9Vh3mV8jP0b/4wO/YUPiT/8i0MLf55lSQAAAABJRU5ErkJggg=="); + QUrl url = QUrl::fromEncoded(requestUrl.toLatin1()); + QNetworkRequest req(url); + QNetworkReplyPtr reply; + + RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, req, reply)); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::NoError); + + QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), qint64(598)); +} + +void tst_QNetworkReply::getFromHttpIntoBuffer_data() +{ + QTest::addColumn("url"); + + QTest::newRow("rfc-internal") << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"); +} + +// Please note that the whole "zero copy" download buffer API is private right now. Do not use it. +void tst_QNetworkReply::getFromHttpIntoBuffer() +{ + QFETCH(QUrl, url); + QNetworkRequest request(url); + request.setAttribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute, 1024*128); // 128 kB + + QNetworkAccessManager manager; + QNetworkReply *reply = manager.get(request); + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(reply->isFinished()); + + QFile reference(SRCDIR "/rfc3252.txt"); + QVERIFY(reference.open(QIODevice::ReadOnly)); + + QCOMPARE(reference.bytesAvailable(), reply->bytesAvailable()); + QCOMPARE(reference.size(), reply->size()); + + // Compare the memory buffer + QVariant downloadBufferAttribute = reply->attribute(QNetworkRequest::DownloadBufferAttribute); + QVERIFY(downloadBufferAttribute.isValid()); + QSharedPointer sharedPointer = downloadBufferAttribute.value >(); + bool memoryComparison = + (0 == memcmp(static_cast(reference.readAll().data()), + sharedPointer.data(), reference.size())); + QVERIFY(memoryComparison); + + // Make sure the normal reading works + reference.seek(0); + QCOMPARE(reply->read(42), reference.read(42)); + QCOMPARE(reply->getChar(0), reference.getChar(0)); + QCOMPARE(reply->peek(23), reference.peek(23)); + QCOMPARE(reply->readLine(), reference.readLine()); + QCOMPARE(reference.bytesAvailable(), reply->bytesAvailable()); + QCOMPARE(reply->readAll(), reference.readAll()); + QVERIFY(reply->atEnd()); +} + +// FIXME we really need to consolidate all those server implementations +class GetFromHttpIntoBuffer2Server : QObject { + Q_OBJECT + qint64 dataSize; + qint64 dataSent; + QTcpServer server; + QTcpSocket *client; + bool serverSendsContentLength; + bool chunkedEncoding; + +public: + GetFromHttpIntoBuffer2Server (qint64 ds, bool sscl, bool ce) : dataSize(ds), dataSent(0), + client(0), serverSendsContentLength(sscl), chunkedEncoding(ce) { + server.listen(); + connect(&server, SIGNAL(newConnection()), this, SLOT(newConnectionSlot())); + } + + int serverPort() { + return server.serverPort(); + } + +public slots: + + void newConnectionSlot() { + client = server.nextPendingConnection(); + client->setParent(this); + connect(client, SIGNAL(readyRead()), this, SLOT(readyReadSlot())); + connect(client, SIGNAL(bytesWritten(qint64)), this, SLOT(bytesWrittenSlot(qint64))); + } + + void readyReadSlot() { + client->readAll(); + client->write("HTTP/1.0 200 OK\n"); + if (serverSendsContentLength) + client->write(QString("Content-Length: " + QString::number(dataSize) + "\n").toAscii()); + if (chunkedEncoding) + client->write(QString("Transfer-Encoding: chunked\n").toAscii()); + client->write("Connection: close\n\n"); + } + + void bytesWrittenSlot(qint64 amount) { + Q_UNUSED(amount); + if (dataSent == dataSize && client) { + // close eventually + + // chunked encoding: we have to send a last "empty" chunk + if (chunkedEncoding) + client->write(QString("0\r\n\r\n").toAscii()); + + client->disconnectFromHost(); + server.close(); + client = 0; + return; + } + + // send data + if (client && client->bytesToWrite() < 100*1024 && dataSent < dataSize) { + qint64 amount = qMin(qint64(16*1024), dataSize - dataSent); + QByteArray data(amount, '@'); + + if (chunkedEncoding) { + client->write(QString(QString("%1").arg(amount,0,16).toUpper() + "\r\n").toAscii()); + client->write(data.constData(), amount); + client->write(QString("\r\n").toAscii()); + } else { + client->write(data.constData(), amount); + } + + dataSent += amount; + } + } +}; + +class GetFromHttpIntoBuffer2Client : QObject { + Q_OBJECT +private: + bool useDownloadBuffer; + QNetworkReply *reply; + qint64 uploadSize; + QList bytesAvailableList; +public: + GetFromHttpIntoBuffer2Client (QNetworkReply *reply, bool useDownloadBuffer, qint64 uploadSize) + : useDownloadBuffer(useDownloadBuffer), reply(reply), uploadSize(uploadSize) + { + connect(reply, SIGNAL(metaDataChanged()), this, SLOT(metaDataChangedSlot())); + connect(reply, SIGNAL(readyRead()), this, SLOT(readyReadSlot())); + connect(reply, SIGNAL(finished()), this, SLOT(finishedSlot())); + } + + public slots: + void metaDataChangedSlot() { + if (useDownloadBuffer) { + QSharedPointer sharedPointer = qvariant_cast >(reply->attribute(QNetworkRequest::DownloadBufferAttribute)); + QVERIFY(!sharedPointer.isNull()); // It will be 0 if it failed + } + + // metaDataChanged needs to come before everything else + QVERIFY(bytesAvailableList.isEmpty()); + } + + void readyReadSlot() { + QVERIFY(!reply->isFinished()); + + qint64 bytesAvailable = reply->bytesAvailable(); + + // bytesAvailable must never be 0 + QVERIFY(bytesAvailable != 0); + + if (bytesAvailableList.length() < 5) { + // We assume that the first few times the bytes available must be less than the complete size, e.g. + // the bytesAvailable() function works correctly in case of a downloadBuffer. + QVERIFY(bytesAvailable < uploadSize); + } + if (!bytesAvailableList.isEmpty()) { + // Also check that the same bytesAvailable is not coming twice in a row + QVERIFY(bytesAvailableList.last() != bytesAvailable); + } + + bytesAvailableList.append(bytesAvailable); + // Add bytesAvailable to a list an parse + } + + void finishedSlot() { + // We should have already received all readyRead + QVERIFY(!bytesAvailableList.isEmpty()); + QVERIFY(bytesAvailableList.last() == uploadSize); + } +}; + +void tst_QNetworkReply::getFromHttpIntoBuffer2_data() +{ + QTest::addColumn("useDownloadBuffer"); + + QTest::newRow("use-download-buffer") << true; + QTest::newRow("do-not-use-download-buffer") << false; +} + +// This test checks mostly that signal emissions are in correct order +// Please note that the whole "zero copy" download buffer API is private right now. Do not use it. +void tst_QNetworkReply::getFromHttpIntoBuffer2() +{ + QFETCH(bool, useDownloadBuffer); + + // On my Linux Desktop the results are already visible with 128 kB, however we use this to have good results. +#if defined(Q_OS_SYMBIAN) || defined(Q_WS_WINCE_WM) + // Show some mercy to non-desktop platform/s + enum {UploadSize = 4*1024*1024}; // 4 MB +#else + enum {UploadSize = 32*1024*1024}; // 32 MB +#endif + + GetFromHttpIntoBuffer2Server server(UploadSize, true, false); + + QNetworkRequest request(QUrl("http://127.0.0.1:" + QString::number(server.serverPort()) + "/?bare=1")); + if (useDownloadBuffer) + request.setAttribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute, 1024*1024*128); // 128 MB is max allowed + + QNetworkAccessManager manager; + QNetworkReplyPtr reply = manager.get(request); + + GetFromHttpIntoBuffer2Client client(reply, useDownloadBuffer, UploadSize); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection); + QTestEventLoop::instance().enterLoop(40); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QVERIFY(!QTestEventLoop::instance().timeout()); +} + + +void tst_QNetworkReply::getFromHttpIntoBufferCanReadLine() +{ + QString header("HTTP/1.0 200 OK\r\nContent-Length: 7\r\n\r\nxxx\nxxx"); + + MiniHttpServer server(header.toAscii()); + server.doClose = true; + + QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort()))); + request.setAttribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute, 1024*1024*128); // 128 MB is max allowed + QNetworkReplyPtr reply = manager.get(request); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(reply->error(), QNetworkReply::NoError); + QVERIFY(reply->canReadLine()); + QCOMPARE(reply->read(1), QByteArray("x")); + QVERIFY(reply->canReadLine()); + QCOMPARE(reply->read(3), QByteArray("xx\n")); + QVERIFY(!reply->canReadLine()); + QCOMPARE(reply->readAll(), QByteArray("xxx")); + QVERIFY(!reply->canReadLine()); +} + + + +// Is handled somewhere else too, introduced this special test to have it more accessible +void tst_QNetworkReply::ioGetFromHttpWithoutContentLength() +{ + QByteArray dataToSend("HTTP/1.0 200 OK\r\n\r\nHALLO! 123!"); + MiniHttpServer server(dataToSend); + server.doClose = true; + + QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort()))); + QNetworkReplyPtr reply = manager.get(request); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(reply->url(), request.url()); + QVERIFY(reply->isFinished()); + QVERIFY(reply->error() == QNetworkReply::NoError); +} + +// Is handled somewhere else too, introduced this special test to have it more accessible +void tst_QNetworkReply::ioGetFromHttpBrokenChunkedEncoding() +{ + // This is wrong chunked encoding because of the X. What actually has to follow is \r\n + // and then the declaration of the final 0 chunk + QByteArray dataToSend("HTTP/1.0 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n3\r\nABCX"); + MiniHttpServer server(dataToSend); + server.doClose = false; // FIXME + + QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort()))); + QNetworkReplyPtr reply = manager.get(request); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + + QEXPECT_FAIL(0, "We should close the socket and not just do nothing", Continue); + QVERIFY(!QTestEventLoop::instance().timeout()); + QEXPECT_FAIL(0, "We should close the socket and not just do nothing", Continue); + QVERIFY(reply->isFinished()); + QCOMPARE(reply->error(), QNetworkReply::NoError); +} + +// TODO: +// Prepare a gzip that has one chunk that expands to the size mentioned in the bugreport. +// Then have a custom HTTP server that waits after this chunk so the returning gets +// triggered. +void tst_QNetworkReply::qtbug12908compressedHttpReply() +{ + QString header("HTTP/1.0 200 OK\r\nContent-Encoding: gzip\r\nContent-Length: 63\r\n\r\n"); + + // dd if=/dev/zero of=qtbug-12908 bs=16384 count=1 && gzip qtbug-12908 && base64 -w 0 qtbug-12908.gz + QString encodedFile("H4sICDdDaUwAA3F0YnVnLTEyOTA4AO3BMQEAAADCoPVPbQwfoAAAAAAAAAAAAAAAAAAAAIC3AYbSVKsAQAAA"); + QByteArray decodedFile = QByteArray::fromBase64(encodedFile.toAscii()); + QCOMPARE(decodedFile.size(), 63); + + MiniHttpServer server(header.toAscii() + decodedFile); + server.doClose = true; + + QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort()))); + QNetworkReplyPtr reply = manager.get(request); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->size(), qint64(16384)); + QCOMPARE(reply->readAll(), QByteArray(16384, '\0')); +} + +void tst_QNetworkReply::compressedHttpReplyBrokenGzip() +{ + QString header("HTTP/1.0 200 OK\r\nContent-Encoding: gzip\r\nContent-Length: 63\r\n\r\n"); + + // dd if=/dev/zero of=qtbug-12908 bs=16384 count=1 && gzip qtbug-12908 && base64 -w 0 qtbug-12908.gz + // Then change "BMQ" to "BMX" + QString encodedFile("H4sICDdDaUwAA3F0YnVnLTEyOTA4AO3BMXEAAADCoPVPbQwfoAAAAAAAAAAAAAAAAAAAAIC3AYbSVKsAQAAA"); + QByteArray decodedFile = QByteArray::fromBase64(encodedFile.toAscii()); + QCOMPARE(decodedFile.size(), 63); + + MiniHttpServer server(header.toAscii() + decodedFile); + server.doClose = true; + + QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort()))); + QNetworkReplyPtr reply = manager.get(request); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(reply->error(), QNetworkReply::ProtocolFailure); +} + +// TODO add similar test for FTP +void tst_QNetworkReply::getFromUnreachableIp() +{ + QNetworkAccessManager manager; + + QNetworkRequest request(QUrl("http://255.255.255.255/42/23/narf/narf/narf")); + QNetworkReplyPtr reply = manager.get(request); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QVERIFY(reply->error() != QNetworkReply::NoError); +} + +void tst_QNetworkReply::qtbug4121unknownAuthentication() +{ + MiniHttpServer server(QByteArray("HTTP/1.1 401 bla\r\nWWW-Authenticate: crap\r\nContent-Length: 0\r\n\r\n")); + server.doClose = false; + + QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort()))); + QNetworkAccessManager manager; + QNetworkReplyPtr reply = manager.get(request); + + qRegisterMetaType("QNetworkReply*"); + qRegisterMetaType("QAuthenticator*"); + QSignalSpy authSpy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*))); + QSignalSpy finishedSpy(&manager, SIGNAL(finished(QNetworkReply*))); + qRegisterMetaType("QNetworkReply::NetworkError"); + QSignalSpy errorSpy(reply, SIGNAL(error(QNetworkReply::NetworkError))); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(authSpy.count(), 0); + QCOMPARE(finishedSpy.count(), 1); + QCOMPARE(errorSpy.count(), 1); + + QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError); +} + +class QtBug13431Helper : public QObject { + Q_OBJECT +public: + QNetworkReply* m_reply; + QTimer m_dlTimer; +public slots: + void replyFinished(QNetworkReply*) { + QTestEventLoop::instance().exitLoop(); + } + + void onReadAndReschedule() { + const qint64 bytesReceived = m_reply->bytesAvailable(); + if (bytesReceived && m_reply->readBufferSize()) { + QByteArray data = m_reply->read(bytesReceived); + // reschedule read + const int millisecDelay = static_cast(bytesReceived * 1000 / m_reply->readBufferSize()); + m_dlTimer.start(millisecDelay); + } + else { + // reschedule read + m_dlTimer.start(200); + } + } +}; + +void tst_QNetworkReply::qtbug13431replyThrottling() +{ + QtBug13431Helper helper; + + QNetworkAccessManager nam; + connect(&nam, SIGNAL(finished(QNetworkReply*)), &helper, SLOT(replyFinished(QNetworkReply*))); + + // Download a bigger file + QNetworkRequest netRequest(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/bigfile")); + helper.m_reply = nam.get(netRequest); + // Set the throttle + helper.m_reply->setReadBufferSize(36000); + + // Schedule a timer that tries to read + + connect(&helper.m_dlTimer, SIGNAL(timeout()), &helper, SLOT(onReadAndReschedule())); + helper.m_dlTimer.setSingleShot(true); + helper.m_dlTimer.start(0); + + QTestEventLoop::instance().enterLoop(30); + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(helper.m_reply->isFinished()); + QCOMPARE(helper.m_reply->error(), QNetworkReply::NoError); +} + +void tst_QNetworkReply::httpWithNoCredentialUsage() +{ + QNetworkRequest request(QUrl("http://httptest:httptest@" + QtNetworkSettings::serverName() + "/qtest/protected/cgi-bin/md5sum.cgi")); + // Do not use credentials + request.setAttribute(QNetworkRequest::AuthenticationReuseAttribute, QNetworkRequest::Manual); + QNetworkAccessManager manager; + QNetworkReplyPtr reply = manager.get(request); + + qRegisterMetaType("QNetworkReply*"); + qRegisterMetaType("QAuthenticator*"); + QSignalSpy authSpy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*))); + QSignalSpy finishedSpy(&manager, SIGNAL(finished(QNetworkReply*))); + qRegisterMetaType("QNetworkReply::NetworkError"); + QSignalSpy errorSpy(reply, SIGNAL(error(QNetworkReply::NetworkError))); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + // We check if authenticationRequired was emitted, however we do not anything in it so it should be 401 + QCOMPARE(authSpy.count(), 1); + QCOMPARE(finishedSpy.count(), 1); + QCOMPARE(errorSpy.count(), 1); + + QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError); +} + +void tst_QNetworkReply::qtbug15311doubleContentLength() +{ + QByteArray response("HTTP/1.0 200 OK\r\nContent-Length: 3\r\nServer: bogus\r\nContent-Length: 3\r\n\r\nABC"); + MiniHttpServer server(response); + server.doClose = true; + + QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort()))); + QNetworkReplyPtr reply = manager.get(request); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(reply->isFinished()); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->size(), qint64(3)); + QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), qint64(3)); + QCOMPARE(reply->rawHeader("Content-length"), QByteArray("3, 3")); + QCOMPARE(reply->readAll(), QByteArray("ABC")); +} + +void tst_QNetworkReply::qtbug18232gzipContentLengthZero() +{ + QByteArray response("HTTP/1.0 200 OK\r\nContent-Encoding: gzip\r\nContent-Length: 0\r\n\r\n"); + MiniHttpServer server(response); + server.doClose = true; + + QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort()))); + QNetworkReplyPtr reply = manager.get(request); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(reply->isFinished()); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->size(), qint64(0)); + QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), qint64(0)); + QCOMPARE(reply->readAll(), QByteArray()); +} + +void tst_QNetworkReply::synchronousRequest_data() +{ + QTest::addColumn("url"); + QTest::addColumn("expected"); + QTest::addColumn("checkContentLength"); + QTest::addColumn("mimeType"); + + // ### cache, auth, proxies + + QTest::newRow("http") + << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt") + << QString("file:" SRCDIR "/rfc3252.txt") + << true + << QString("text/plain"); + + QTest::newRow("http-gzip") + << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/deflate/rfc3252.txt") + << QString("file:" SRCDIR "/rfc3252.txt") + << false // don't check content length, because it's gzip encoded + // ### we would need to enflate (un-deflate) the file content and compare the sizes + << QString("text/plain"); + +#ifndef QT_NO_OPENSSL + QTest::newRow("https") + << QUrl("https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt") + << QString("file:" SRCDIR "/rfc3252.txt") + << true + << QString("text/plain"); +#endif + + QTest::newRow("data") + << QUrl(QString::fromLatin1("data:text/plain,hello world")) + << QString("data:hello world") + << true // check content length + << QString("text/plain"); + + QTest::newRow("simple-file") + << QUrl::fromLocalFile(SRCDIR "/rfc3252.txt") + << QString("file:" SRCDIR "/rfc3252.txt") + << true + << QString(); +} + +// FIXME add testcase for failing network etc +void tst_QNetworkReply::synchronousRequest() +{ + QFETCH(QUrl, url); + QFETCH(QString, expected); + QFETCH(bool, checkContentLength); + QFETCH(QString, mimeType); + + QNetworkRequest request(url); + +#ifndef QT_NO_OPENSSL + // workaround for HTTPS requests: add self-signed server cert to list of CA certs, + // since we cannot react to the sslErrors() signal + // to fix this properly we would need to have an ignoreSslErrors() method in the + // QNetworkRequest, see http://bugreports.qt.nokia.com/browse/QTBUG-14774 + if (url.scheme() == "https") { + QSslConfiguration sslConf; + QList certs = QSslCertificate::fromPath(SRCDIR "/certs/qt-test-server-cacert.pem"); + sslConf.setCaCertificates(certs); + request.setSslConfiguration(sslConf); + } +#endif + + request.setAttribute( + QNetworkRequest::SynchronousRequestAttribute, + true); + + QNetworkReplyPtr reply; + QSignalSpy finishedSpy(&manager, SIGNAL(finished(QNetworkReply*))); + QSignalSpy sslErrorsSpy(&manager, SIGNAL(sslErrors(QNetworkReply*,QList))); + RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply, 0)); + QVERIFY(reply->isFinished()); + QCOMPARE(finishedSpy.count(), 0); + QCOMPARE(sslErrorsSpy.count(), 0); + + QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader).toString(), mimeType); + + QByteArray expectedContent; + + if (expected.startsWith("file:")) { + QString path = expected.mid(5); + QFile file(path); + file.open(QIODevice::ReadOnly); + expectedContent = file.readAll(); + } else if (expected.startsWith("data:")) { + expectedContent = expected.mid(5).toUtf8(); + } + + if (checkContentLength) + QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), qint64(expectedContent.size())); + QCOMPARE(reply->readAll(), expectedContent); + + reply->deleteLater(); +} + +#ifndef QT_NO_OPENSSL +void tst_QNetworkReply::synchronousRequestSslFailure() +{ + // test that SSL won't be accepted with self-signed certificate, + // and that we do not emit the sslError signal (in the manager that is, + // in the reply we don't care) + + QUrl url("https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"); + QNetworkRequest request(url); + request.setAttribute( + QNetworkRequest::SynchronousRequestAttribute, + true); + QNetworkReplyPtr reply; + QSignalSpy sslErrorsSpy(&manager, SIGNAL(sslErrors(QNetworkReply *, const QList &))); + runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply, 0); + QVERIFY(reply->isFinished()); + QCOMPARE(reply->error(), QNetworkReply::SslHandshakeFailedError); + QCOMPARE(sslErrorsSpy.count(), 0); +} +#endif + +class HttpAbortHelper : public QObject +{ + Q_OBJECT +public: + HttpAbortHelper(QNetworkReply *parent) + : QObject(parent) + { + mReply = parent; + connect(parent, SIGNAL(readyRead()), this, SLOT(readyRead())); + } + + ~HttpAbortHelper() + { + } + +public slots: + void readyRead() + { + mReply->abort(); + QMetaObject::invokeMethod(&QTestEventLoop::instance(), "exitLoop", Qt::QueuedConnection); + } + +private: + QNetworkReply *mReply; +}; + +void tst_QNetworkReply::httpAbort() +{ + // FIXME Also implement one where we do a big upload and then abort(). + // It must not crash either. + + // Abort after the first readyRead() + QNetworkRequest request("http://" + QtNetworkSettings::serverName() + "/qtest/bigfile"); + QNetworkReplyPtr reply; + reply = manager.get(request); + HttpAbortHelper replyHolder(reply); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + QCOMPARE(reply->error(), QNetworkReply::OperationCanceledError); + QVERIFY(reply->isFinished()); + + // Abort immediately after the get() + QNetworkReplyPtr reply2 = manager.get(request); + connect(reply2, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + reply2->abort(); + QCOMPARE(reply2->error(), QNetworkReply::OperationCanceledError); + QVERIFY(reply2->isFinished()); + + // Abort after the finished() + QNetworkRequest request3("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"); + QNetworkReplyPtr reply3 = manager.get(request3); + connect(reply3, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(reply3->isFinished()); + reply3->abort(); + QCOMPARE(reply3->error(), QNetworkReply::NoError); +} + +void tst_QNetworkReply::dontInsertPartialContentIntoTheCache() +{ + QByteArray reply206 = + "HTTP/1.0 206\r\n" + "Connection: keep-alive\r\n" + "Content-Type: text/plain\r\n" + "Cache-control: no-cache\r\n" + "Content-Range: bytes 2-6/8\r\n" + "Content-length: 4\r\n" + "\r\n" + "load"; + + MiniHttpServer server(reply206); + server.doClose = false; + + MySpyMemoryCache *memoryCache = new MySpyMemoryCache(&manager); + manager.setCache(memoryCache); + + QUrl url = "http://localhost:" + QString::number(server.serverPort()); + QNetworkRequest request(url); + request.setRawHeader("Range", "bytes=2-6"); + + QNetworkReplyPtr reply = manager.get(request); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QVERIFY(server.totalConnections > 0); + QCOMPARE(reply->readAll().constData(), "load"); + QCOMPARE(memoryCache->m_insertedUrls.count(), 0); +} + +void tst_QNetworkReply::httpUserAgent() +{ + QByteArray response("HTTP/1.0 200 OK\r\n\r\n"); + MiniHttpServer server(response); + server.doClose = true; + + QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort()))); + request.setHeader(QNetworkRequest::UserAgentHeader, "abcDEFghi"); + QNetworkReplyPtr reply = manager.get(request); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(reply->isFinished()); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QVERIFY(server.receivedData.contains("\r\nUser-Agent: abcDEFghi\r\n")); +} + + +// NOTE: This test must be last testcase in tst_qnetworkreply! +void tst_QNetworkReply::parentingRepliesToTheApp() +{ + QNetworkRequest request (QUrl("http://" + QtNetworkSettings::serverName())); + manager.get(request)->setParent(this); // parent to this object + manager.get(request)->setParent(qApp); // parent to the app +} + +QTEST_MAIN(tst_QNetworkReply) + +#include "tst_qnetworkreply.moc" diff --git a/tests/auto/network/access/qnetworkrequest/.gitignore b/tests/auto/network/access/qnetworkrequest/.gitignore new file mode 100644 index 0000000000..c814099c96 --- /dev/null +++ b/tests/auto/network/access/qnetworkrequest/.gitignore @@ -0,0 +1 @@ +tst_qnetworkrequest diff --git a/tests/auto/network/access/qnetworkrequest/qnetworkrequest.pro b/tests/auto/network/access/qnetworkrequest/qnetworkrequest.pro new file mode 100644 index 0000000000..f96fd3bb02 --- /dev/null +++ b/tests/auto/network/access/qnetworkrequest/qnetworkrequest.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +SOURCES += tst_qnetworkrequest.cpp + +QT = core network +symbian: TARGET.CAPABILITY = NetworkServices diff --git a/tests/auto/network/access/qnetworkrequest/tst_qnetworkrequest.cpp b/tests/auto/network/access/qnetworkrequest/tst_qnetworkrequest.cpp new file mode 100644 index 0000000000..90527163e2 --- /dev/null +++ b/tests/auto/network/access/qnetworkrequest/tst_qnetworkrequest.cpp @@ -0,0 +1,496 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QList) +Q_DECLARE_METATYPE(QNetworkRequest::KnownHeaders) +Q_DECLARE_METATYPE(QVariant) + +class tst_QNetworkRequest: public QObject +{ + Q_OBJECT + +private slots: + void ctor_data(); + void ctor(); + void setUrl_data(); + void setUrl(); + void setRawHeader_data(); + void setRawHeader(); + void rawHeaderList_data(); + void rawHeaderList(); + void setHeader_data(); + void setHeader(); + void rawHeaderParsing_data(); + void rawHeaderParsing(); + void originatingObject(); + + void removeHeader(); +}; + +QT_BEGIN_NAMESPACE + +namespace QTest { + template<> + char *toString(const QNetworkCookie &cookie) + { + return qstrdup(cookie.toRawForm()); + } + + template<> + char *toString(const QList &list) + { + QString result = "QList("; + bool first = true; + foreach (QNetworkCookie cookie, list) { + if (!first) + result += ", "; + first = false; + result += QString::fromLatin1("QNetworkCookie(%1)").arg(QLatin1String(cookie.toRawForm())); + } + + return qstrdup(result.append(')').toLocal8Bit()); + } +} + +QT_END_NAMESPACE + +void tst_QNetworkRequest::ctor_data() +{ + QTest::addColumn("url"); + + QTest::newRow("nothing") << QUrl(); + QTest::newRow("empty") << QUrl(); + QTest::newRow("http") << QUrl("http://qt.nokia.com"); +} + +void tst_QNetworkRequest::ctor() +{ + QFETCH(QUrl, url); + + if (qstrcmp(QTest::currentDataTag(), "nothing") == 0) { + QNetworkRequest request; + QCOMPARE(request.url(), url); + } else { + QNetworkRequest request(url); + QCOMPARE(request.url(), url); + } +} + +void tst_QNetworkRequest::setUrl_data() +{ + ctor_data(); +} + +void tst_QNetworkRequest::setUrl() +{ + QFETCH(QUrl, url); + QNetworkRequest request; + + if (qstrcmp(QTest::currentDataTag(), "nothing") != 0) + request.setUrl(url); + + QCOMPARE(request.url(), url); +} + +void tst_QNetworkRequest::setRawHeader_data() +{ + QTest::addColumn("header"); + QTest::addColumn("value"); + QTest::addColumn("headerToGet"); + QTest::addColumn("expectedValue"); + QTest::addColumn("hasHeader"); + + QTest::newRow("null-header") << QByteArray() << QByteArray("abc") + << QByteArray() << QByteArray() << false; + QTest::newRow("empty-header") << QByteArray("") << QByteArray("abc") + << QByteArray("") << QByteArray() << false; + QTest::newRow("null-value") << QByteArray("foo") << QByteArray() + << QByteArray("foo") << QByteArray() << false; + QTest::newRow("empty-value") << QByteArray("foo") << QByteArray("") + << QByteArray("foo") << QByteArray("") << true; + QTest::newRow("empty-value-vs-null") << QByteArray("foo") << QByteArray("") + << QByteArray("foo") << QByteArray() << true; + + QTest::newRow("UPPER-UPPER") << QByteArray("FOO") << QByteArray("abc") + << QByteArray("FOO") << QByteArray("abc") << true; + QTest::newRow("UPPER-Mixed") << QByteArray("FOO") << QByteArray("abc") + << QByteArray("Foo") << QByteArray("abc") << true; + QTest::newRow("UPPER-lower") << QByteArray("FOO") << QByteArray("abc") + << QByteArray("foo") << QByteArray("abc") << true; + QTest::newRow("Mixed-UPPER") << QByteArray("Foo") << QByteArray("abc") + << QByteArray("FOO") << QByteArray("abc") << true; + QTest::newRow("Mixed-Mixed") << QByteArray("Foo") << QByteArray("abc") + << QByteArray("Foo") << QByteArray("abc") << true; + QTest::newRow("Mixed-lower") << QByteArray("Foo") << QByteArray("abc") + << QByteArray("foo") << QByteArray("abc") << true; + QTest::newRow("lower-UPPER") << QByteArray("foo") << QByteArray("abc") + << QByteArray("FOO") << QByteArray("abc") << true; + QTest::newRow("lower-Mixed") << QByteArray("foo") << QByteArray("abc") + << QByteArray("Foo") << QByteArray("abc") << true; + QTest::newRow("lower-lower") << QByteArray("foo") << QByteArray("abc") + << QByteArray("foo") << QByteArray("abc") << true; +} + +void tst_QNetworkRequest::setRawHeader() +{ + QFETCH(QByteArray, header); + QFETCH(QByteArray, value); + QFETCH(QByteArray, headerToGet); + QFETCH(QByteArray, expectedValue); + QFETCH(bool, hasHeader); + + QNetworkRequest request; + request.setRawHeader(header, value); + + QCOMPARE(request.hasRawHeader(headerToGet), hasHeader); + QCOMPARE(request.rawHeader(headerToGet), expectedValue); +} + +void tst_QNetworkRequest::rawHeaderList_data() +{ + QTest::addColumn >("set"); + QTest::addColumn >("expected"); + + QTest::newRow("empty") << QList() << QList(); + + QList set; + QList expected; + + set << "foo"; + expected = set; + QTest::newRow("one") << set << expected; + + set << "bar"; + expected = set; + QTest::newRow("two") << set << expected; + + set.clear(); + expected.clear(); + set << "foo" << "foo"; + expected << "foo"; + QTest::newRow("repeated") << set << expected; + + set.clear(); + expected.clear(); + set << "foo" << "bar" << "foo"; + expected << "bar" << "foo"; + QTest::newRow("repeated-interleaved") << set << expected; +} + +void tst_QNetworkRequest::rawHeaderList() +{ + QFETCH(QList, set); + QFETCH(QList, expected); + + QNetworkRequest request; + foreach (QByteArray header, set) + request.setRawHeader(header, "a value"); + + QList got = request.rawHeaderList(); + QCOMPARE(got.size(), expected.size()); + for (int i = 0; i < got.size(); ++i) + QCOMPARE(got.at(i), expected.at(i)); +} + +void tst_QNetworkRequest::setHeader_data() +{ + QTest::addColumn("cookedHeader"); + QTest::addColumn("cookedValue"); + QTest::addColumn("success"); + QTest::addColumn("rawHeader"); + QTest::addColumn("rawValue"); + + QTest::newRow("Content-Type-Null") << QNetworkRequest::ContentTypeHeader << QVariant() + << false << "Content-Type" << ""; + QTest::newRow("Content-Type-String") << QNetworkRequest::ContentTypeHeader << QVariant("text/html") + << true + << "Content-Type" << "text/html"; + QTest::newRow("Content-Type-ByteArray") << QNetworkRequest::ContentTypeHeader + << QVariant("text/html") << true + << "Content-Type" << "text/html"; + + QTest::newRow("Content-Length-Int") << QNetworkRequest::ContentLengthHeader << QVariant(1) + << true << "Content-Length" << "1"; + QTest::newRow("Content-Length-Int64") << QNetworkRequest::ContentLengthHeader << QVariant(qint64(1)) + << true << "Content-Length" << "1"; + + QTest::newRow("Location-String") << QNetworkRequest::LocationHeader << QVariant("http://foo/with space") + << true << "Location" << "http://foo/with space"; + QTest::newRow("Location-ByteArray") << QNetworkRequest::LocationHeader + << QVariant("http://foo/with space") + << true << "Location" << "http://foo/with space"; + QTest::newRow("Location-Url") << QNetworkRequest::LocationHeader + << QVariant(QUrl("http://foo/with space")) + << true << "Location" << "http://foo/with%20space"; + + QTest::newRow("Last-Modified-Date") << QNetworkRequest::LastModifiedHeader + << QVariant(QDate(2007, 11, 01)) + << true << "Last-Modified" + << "Thu, 01 Nov 2007 00:00:00 GMT"; + QTest::newRow("Last-Modified-DateTime") << QNetworkRequest::LastModifiedHeader + << QVariant(QDateTime(QDate(2007, 11, 01), + QTime(18, 8, 30), + Qt::UTC)) + << true << "Last-Modified" + << "Thu, 01 Nov 2007 18:08:30 GMT"; + + QNetworkCookie cookie; + cookie.setName("a"); + cookie.setValue("b"); + QTest::newRow("Cookie-1") << QNetworkRequest::CookieHeader + << qVariantFromValue(QList() << cookie) + << true << "Cookie" + << "a=b"; + QTest::newRow("SetCookie-1") << QNetworkRequest::SetCookieHeader + << qVariantFromValue(QList() << cookie) + << true << "Set-Cookie" + << "a=b"; + + cookie.setPath("/"); + QTest::newRow("Cookie-2") << QNetworkRequest::CookieHeader + << qVariantFromValue(QList() << cookie) + << true << "Cookie" + << "a=b"; + QTest::newRow("SetCookie-2") << QNetworkRequest::SetCookieHeader + << qVariantFromValue(QList() << cookie) + << true << "Set-Cookie" + << "a=b; path=/"; + + QNetworkCookie cookie2; + cookie2.setName("c"); + cookie2.setValue("d"); + QTest::newRow("Cookie-3") << QNetworkRequest::CookieHeader + << qVariantFromValue(QList() << cookie << cookie2) + << true << "Cookie" + << "a=b; c=d"; + QTest::newRow("SetCookie-3") << QNetworkRequest::SetCookieHeader + << qVariantFromValue(QList() << cookie << cookie2) + << true << "Set-Cookie" + << "a=b; path=/, c=d"; +} + +void tst_QNetworkRequest::setHeader() +{ + QFETCH(QNetworkRequest::KnownHeaders, cookedHeader); + QFETCH(QVariant, cookedValue); + QFETCH(bool, success); + QFETCH(QString, rawHeader); + QFETCH(QString, rawValue); + + QNetworkRequest request; + request.setHeader(cookedHeader, cookedValue); + + QCOMPARE(request.header(cookedHeader).isNull(), !success); + QCOMPARE(request.hasRawHeader(rawHeader.toLatin1()), success); + QCOMPARE(request.rawHeader(rawHeader.toLatin1()).isEmpty(), !success); + + if (success) { + QCOMPARE(request.header(cookedHeader), cookedValue); + QCOMPARE(QString(request.rawHeader(rawHeader.toLatin1())), rawValue); + } +} + +void tst_QNetworkRequest::rawHeaderParsing_data() +{ + QTest::addColumn("cookedHeader"); + QTest::addColumn("cookedValue"); + QTest::addColumn("success"); + QTest::addColumn("rawHeader"); + QTest::addColumn("rawValue"); + + QTest::newRow("Content-Type") << QNetworkRequest::ContentTypeHeader << QVariant("text/html") + << true + << "Content-Type" << "text/html"; + QTest::newRow("Content-Length") << QNetworkRequest::ContentLengthHeader << QVariant(qint64(1)) + << true << "Content-Length" << " 1 "; + QTest::newRow("Location") << QNetworkRequest::LocationHeader + << QVariant(QUrl("http://foo/with space")) + << true << "Location" << "http://foo/with%20space"; + QTest::newRow("Last-Modified-RFC1123") << QNetworkRequest::LastModifiedHeader + << QVariant(QDateTime(QDate(1994, 11, 06), + QTime(8, 49, 37), + Qt::UTC)) + << true << "Last-Modified" + << "Sun, 06 Nov 1994 08:49:37 GMT"; + QTest::newRow("Last-Modified-RFC850") << QNetworkRequest::LastModifiedHeader + << QVariant(QDateTime(QDate(1994, 11, 06), + QTime(8, 49, 37), + Qt::UTC)) + << true << "Last-Modified" + << "Sunday, 06-Nov-94 08:49:37 GMT"; + QTest::newRow("Last-Modified-asctime") << QNetworkRequest::LastModifiedHeader + << QVariant(QDateTime(QDate(1994, 11, 06), + QTime(8, 49, 37), + Qt::UTC)) + << true << "Last-Modified" + << "Sun Nov 6 08:49:37 1994"; + + QTest::newRow("Content-Length-invalid1") << QNetworkRequest::ContentLengthHeader << QVariant() + << false << "Content-Length" << "1a"; + QTest::newRow("Content-Length-invalid2") << QNetworkRequest::ContentLengthHeader << QVariant() + << false << "Content-Length" << "a"; + + + QTest::newRow("Location-invalid1") << QNetworkRequest::LocationHeader << QVariant() << false + << "Location" << "abc"; + QTest::newRow("Location-invalid2") << QNetworkRequest::LocationHeader << QVariant() << false + << "Location" << "1http://foo"; + QTest::newRow("Location-invalid3") << QNetworkRequest::LocationHeader << QVariant() << false + << "Location" << "http://foo/%gg"; + + // don't test for invalid dates because we may want to support broken servers in the future + + QNetworkCookie cookie; + cookie.setName("a"); + cookie.setValue("b"); + QTest::newRow("Cookie-1") << QNetworkRequest::CookieHeader + << qVariantFromValue(QList() << cookie) + << true << "Cookie" + << "a=b"; + QTest::newRow("SetCookie-1") << QNetworkRequest::SetCookieHeader + << qVariantFromValue(QList() << cookie) + << true << "Set-Cookie" + << "a=b"; + + cookie.setPath("/"); + QTest::newRow("SetCookie-2") << QNetworkRequest::SetCookieHeader + << qVariantFromValue(QList() << cookie) + << true << "Set-Cookie" + << "a=b; path=/"; + + QNetworkCookie cookie2; + cookie.setPath(""); + cookie2.setName("c"); + cookie2.setValue("d"); + QTest::newRow("Cookie-3") << QNetworkRequest::CookieHeader + << qVariantFromValue(QList() << cookie << cookie2) + << true << "Cookie" + << "a=b; c=d"; + cookie.setPath("/"); + QTest::newRow("SetCookie-3") << QNetworkRequest::SetCookieHeader + << qVariantFromValue(QList() << cookie << cookie2) + << true << "Set-Cookie" + << "a=b; path=/, c=d"; +} + +void tst_QNetworkRequest::rawHeaderParsing() +{ + QFETCH(QNetworkRequest::KnownHeaders, cookedHeader); + QFETCH(QVariant, cookedValue); + QFETCH(bool, success); + QFETCH(QString, rawHeader); + QFETCH(QString, rawValue); + + QNetworkRequest request; + request.setRawHeader(rawHeader.toLatin1(), rawValue.toLatin1()); + + // even if it doesn't parse, it's as a raw header + QVERIFY(request.hasRawHeader(rawHeader.toLatin1())); + QVERIFY(request.hasRawHeader(rawHeader.toLower().toLatin1())); + QCOMPARE(QString(request.rawHeader(rawHeader.toLatin1())), rawValue); + + QCOMPARE(request.header(cookedHeader).isNull(), !success); + if (cookedValue.type() != QVariant::UserType) + QCOMPARE(request.header(cookedHeader), cookedValue); + else if (cookedValue.userType() == qMetaTypeId >()) + QCOMPARE(qvariant_cast >(request.header(cookedHeader)), + qvariant_cast >(cookedValue)); +} + +void tst_QNetworkRequest::removeHeader() +{ + QNetworkRequest request; + + request.setRawHeader("Foo", "1"); + QVERIFY(request.hasRawHeader("Foo")); + QVERIFY(request.hasRawHeader("foo")); + request.setRawHeader("Foo", QByteArray()); + QVERIFY(!request.hasRawHeader("Foo")); + + // same, but remove with different capitalisation + request.setRawHeader("Foo", "1"); + QVERIFY(request.hasRawHeader("Foo")); + QVERIFY(request.hasRawHeader("foo")); + request.setRawHeader("foo", QByteArray()); + QVERIFY(!request.hasRawHeader("Foo")); + + // same, but not the first + request.setRawHeader("Bar", "2"); + request.setRawHeader("Foo", "1"); + QVERIFY(request.hasRawHeader("Foo")); + QVERIFY(request.hasRawHeader("foo")); + request.setRawHeader("foo", QByteArray()); + QVERIFY(!request.hasRawHeader("Foo")); + QVERIFY(request.hasRawHeader("bar")); + + // same, but not the first nor last + request.setRawHeader("Foo", "1"); + request.setRawHeader("Bar", "3"); + QVERIFY(request.hasRawHeader("Foo")); + QVERIFY(request.hasRawHeader("foo")); + request.setRawHeader("foo", QByteArray()); + QVERIFY(!request.hasRawHeader("Foo")); + QVERIFY(request.hasRawHeader("bar")); +} + +void tst_QNetworkRequest::originatingObject() +{ + QNetworkRequest request; + + QVERIFY(!request.originatingObject()); + + { + QObject dummy; + request.setOriginatingObject(&dummy); + QCOMPARE(request.originatingObject(), &dummy); + } + + QVERIFY(!request.originatingObject()); +} + +QTEST_MAIN(tst_QNetworkRequest) +#include "tst_qnetworkrequest.moc" diff --git a/tests/auto/network/bearer/bearer.pro b/tests/auto/network/bearer/bearer.pro new file mode 100644 index 0000000000..872a818e4c --- /dev/null +++ b/tests/auto/network/bearer/bearer.pro @@ -0,0 +1,6 @@ +TEMPLATE=subdirs +SUBDIRS=\ + qnetworkconfiguration \ + qnetworkconfigurationmanager \ + qnetworksession \ + diff --git a/tests/auto/network/bearer/qbearertestcommon.h b/tests/auto/network/bearer/qbearertestcommon.h new file mode 100644 index 0000000000..c16c2016e8 --- /dev/null +++ b/tests/auto/network/bearer/qbearertestcommon.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBEARERTESTCOMMON_H +#define QBEARERTESTCOMMON_H + +// Wait for __expr to happen, while still allowing events to be processed. +#define QTRY_NOOP(__expr) \ + do { \ + const int __step = 50; \ + const int __timeout = 15000; \ + if (!(__expr)) { \ + QTest::qWait(0); \ + } \ + for (int __i = 0; __i < __timeout && !(__expr); __i+=__step) { \ + QTest::qWait(__step); \ + } \ + } while(0) + +// Will try to wait for the condition while allowing event processing +#define QTRY_VERIFY(__expr) \ + do { \ + const int __step = 50; \ + const int __timeout = 90000; \ + if (!(__expr)) { \ + QTest::qWait(0); \ + } \ + for (int __i = 0; __i < __timeout && !(__expr); __i+=__step) { \ + QTest::qWait(__step); \ + } \ + QVERIFY(__expr); \ + } while(0) + +// Will try to wait for the condition while allowing event processing +#define QTRY_COMPARE(__expr, __expected) \ + do { \ + const int __step = 50; \ + const int __timeout = 90000; \ + if ((__expr) != (__expected)) { \ + QTest::qWait(0); \ + } \ + for (int __i = 0; __i < __timeout && ((__expr) != (__expected)); __i+=__step) { \ + QTest::qWait(__step); \ + } \ + QCOMPARE(__expr, __expected); \ + } while(0) + +#endif + diff --git a/tests/auto/network/bearer/qnetworkconfiguration/qnetworkconfiguration.pro b/tests/auto/network/bearer/qnetworkconfiguration/qnetworkconfiguration.pro new file mode 100644 index 0000000000..ca463bdc99 --- /dev/null +++ b/tests/auto/network/bearer/qnetworkconfiguration/qnetworkconfiguration.pro @@ -0,0 +1,15 @@ +load(qttest_p4) +SOURCES += tst_qnetworkconfiguration.cpp +HEADERS += ../qbearertestcommon.h + +QT = core network + +symbian { + TARGET.CAPABILITY = NetworkServices NetworkControl ReadUserData +} + +maemo6|maemo5 { + CONFIG += link_pkgconfig + + PKGCONFIG += conninet +} diff --git a/tests/auto/network/bearer/qnetworkconfiguration/tst_qnetworkconfiguration.cpp b/tests/auto/network/bearer/qnetworkconfiguration/tst_qnetworkconfiguration.cpp new file mode 100644 index 0000000000..a8add01e06 --- /dev/null +++ b/tests/auto/network/bearer/qnetworkconfiguration/tst_qnetworkconfiguration.cpp @@ -0,0 +1,311 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include "../qbearertestcommon.h" + +#include +#include + +/* + Although this unit test doesn't use QNetworkAccessManager + this include is used to ensure that bearer continues to compile against + Qt 4.7+ which has a QNetworkConfiguration enabled QNetworkAccessManager +*/ +#include + +#if defined(Q_OS_UNIX) && !defined(QT_NO_ICD) && !defined (Q_OS_SYMBIAN) +#include +#include +#endif + +QT_USE_NAMESPACE +class tst_QNetworkConfiguration : public QObject +{ + Q_OBJECT + +public slots: + void initTestCase(); + void cleanupTestCase(); + +private slots: + void invalidPoint(); + void comparison(); + void children(); + void isRoamingAvailable(); + +private: +#if defined(Q_OS_UNIX) && !defined(QT_NO_ICD) && !defined (Q_OS_SYMBIAN) + Maemo::IAPConf *iapconf; + Maemo::IAPConf *iapconf2; + Maemo::IAPConf *gprsiap; +#define MAX_IAPS 50 + Maemo::IAPConf *iaps[MAX_IAPS]; + QProcess *icd_stub; +#endif +}; + +void tst_QNetworkConfiguration::initTestCase() +{ +#if defined(Q_OS_UNIX) && !defined(QT_NO_ICD) && !defined (Q_OS_SYMBIAN) + iapconf = new Maemo::IAPConf("007"); + iapconf->setValue("ipv4_type", "AUTO"); + iapconf->setValue("wlan_wepkey1", "connt"); + iapconf->setValue("wlan_wepdefkey", 1); + iapconf->setValue("wlan_ssid", QByteArray("JamesBond")); + iapconf->setValue("name", "James Bond"); + iapconf->setValue("type", "WLAN_INFRA"); + + iapconf2 = new Maemo::IAPConf("osso.net"); + iapconf2->setValue("ipv4_type", "AUTO"); + iapconf2->setValue("wlan_wepkey1", "osso.net"); + iapconf2->setValue("wlan_wepdefkey", 1); + iapconf2->setValue("wlan_ssid", QByteArray("osso.net")); + iapconf2->setValue("name", "osso.net"); + iapconf2->setValue("type", "WLAN_INFRA"); + iapconf2->setValue("wlan_security", "WEP"); + + gprsiap = new Maemo::IAPConf("This-is-GPRS-IAP"); + gprsiap->setValue("ask_password", false); + gprsiap->setValue("gprs_accesspointname", "internet"); + gprsiap->setValue("gprs_password", ""); + gprsiap->setValue("gprs_username", ""); + gprsiap->setValue("ipv4_autodns", true); + gprsiap->setValue("ipv4_type", "AUTO"); + gprsiap->setValue("sim_imsi", "244070123456789"); + gprsiap->setValue("name", "MI6"); + gprsiap->setValue("type", "GPRS"); + + /* Create large number of IAPs in the gconf and see what happens */ + fflush(stdout); + printf("Creating %d IAPS: ", MAX_IAPS); + for (int i=0; isetValue("name", QString("test-iap-")+num); + iaps[i]->setValue("type", "WLAN_INFRA"); + iaps[i]->setValue("wlan_ssid", QString(QString("test-ssid-")+num).toAscii()); + iaps[i]->setValue("wlan_security", "WPA_PSK"); + iaps[i]->setValue("EAP_wpa_preshared_passphrase", QString("test-passphrase-")+num); + printf("."); + fflush(stdout); + } + printf("\n"); + fflush(stdout); + + icd_stub = new QProcess(this); + icd_stub->start("/usr/bin/icd2_stub.py"); + QTest::qWait(1000); + + // Add a known network to scan list that icd2 stub returns + QProcess dbus_send; + // 007 network + dbus_send.start("dbus-send --type=method_call --system " + "--dest=com.nokia.icd2 /com/nokia/icd2 " + "com.nokia.icd2.testing.add_available_network " + "string:'' uint32:0 string:'' " + "string:WLAN_INFRA uint32:5000011 array:byte:48,48,55"); + dbus_send.waitForFinished(); + + // osso.net network + dbus_send.start("dbus-send --type=method_call --system " + "--dest=com.nokia.icd2 /com/nokia/icd2 " + "com.nokia.icd2.testing.add_available_network " + "string:'' uint32:0 string:'' " + "string:WLAN_INFRA uint32:83886097 array:byte:111,115,115,111,46,110,101,116"); + dbus_send.waitForFinished(); +#endif +} + +void tst_QNetworkConfiguration::cleanupTestCase() +{ +#if defined(Q_OS_UNIX) && !defined(QT_NO_ICD) && !defined (Q_OS_SYMBIAN) + iapconf->clear(); + delete iapconf; + iapconf2->clear(); + delete iapconf2; + gprsiap->clear(); + delete gprsiap; + + printf("Deleting %d IAPS : ", MAX_IAPS); + for (int i=0; iclear(); + delete iaps[i]; + printf("."); + fflush(stdout); + } + printf("\n"); + qDebug() << "Deleted" << MAX_IAPS << "IAPs"; + + // Terminate icd2 stub + icd_stub->terminate(); + icd_stub->waitForFinished(); +#endif +} + +void tst_QNetworkConfiguration::invalidPoint() +{ + QNetworkConfiguration pt; + + QVERIFY(pt.name().isEmpty()); + QVERIFY(!pt.isValid()); + QVERIFY(pt.type() == QNetworkConfiguration::Invalid); + QVERIFY(!(pt.state() & QNetworkConfiguration::Defined)); + QVERIFY(!(pt.state() & QNetworkConfiguration::Discovered)); + QVERIFY(!(pt.state() & QNetworkConfiguration::Active)); + QVERIFY(!pt.isRoamingAvailable()); + + QNetworkConfiguration pt2(pt); + QVERIFY(pt2.name().isEmpty()); + QVERIFY(!pt2.isValid()); + QVERIFY(pt2.type() == QNetworkConfiguration::Invalid); + QVERIFY(!(pt2.state() & QNetworkConfiguration::Defined)); + QVERIFY(!(pt2.state() & QNetworkConfiguration::Discovered)); + QVERIFY(!(pt2.state() & QNetworkConfiguration::Active)); + QVERIFY(!pt2.isRoamingAvailable()); + +} + +void tst_QNetworkConfiguration::comparison() +{ + //test copy constructor and assignment operator + //compare invalid connection points + QNetworkConfiguration pt1; + QVERIFY(!pt1.isValid()); + QVERIFY(pt1.type() == QNetworkConfiguration::Invalid); + + QNetworkConfiguration pt2(pt1); + QVERIFY(pt1==pt2); + QVERIFY(!(pt1!=pt2)); + QVERIFY(pt1.name() == pt2.name()); + QVERIFY(pt1.isValid() == pt2.isValid()); + QVERIFY(pt1.type() == pt2.type()); + QVERIFY(pt1.state() == pt2.state()); + QVERIFY(pt1.purpose() == pt2.purpose()); + + + QNetworkConfiguration pt3; + pt3 = pt1; + QVERIFY(pt1==pt3); + QVERIFY(!(pt1!=pt3)); + QVERIFY(pt1.name() == pt3.name()); + QVERIFY(pt1.isValid() == pt3.isValid()); + QVERIFY(pt1.type() == pt3.type()); + QVERIFY(pt1.state() == pt3.state()); + QVERIFY(pt1.purpose() == pt3.purpose()); + + //test case must run on machine that has valid connection points + QNetworkConfigurationManager manager; + QList preScanConfigs = manager.allConfigurations(); + + QSignalSpy spy(&manager, SIGNAL(updateCompleted())); + manager.updateConfigurations(); //initiate scans + QTRY_VERIFY(spy.count() == 1); //wait for scan to complete + + QList configs = manager.allConfigurations(QNetworkConfiguration::Discovered); + QVERIFY(configs.count()); + QNetworkConfiguration defaultConfig = manager.defaultConfiguration(); + QVERIFY(defaultConfig.isValid()); + QVERIFY(defaultConfig.type() != QNetworkConfiguration::Invalid); + QVERIFY(!defaultConfig.name().isEmpty()); + + pt3 = defaultConfig; + QVERIFY(defaultConfig==pt3); + QVERIFY(!(defaultConfig!=pt3)); + QVERIFY(defaultConfig.name() == pt3.name()); + QVERIFY(defaultConfig.isValid() == pt3.isValid()); + QVERIFY(defaultConfig.type() == pt3.type()); + QVERIFY(defaultConfig.state() == pt3.state()); + QVERIFY(defaultConfig.purpose() == pt3.purpose()); +} + +void tst_QNetworkConfiguration::children() +{ + QNetworkConfigurationManager manager; + QList configs = manager.allConfigurations(); + + foreach(QNetworkConfiguration c, configs) + { + if ( c.type() == QNetworkConfiguration::ServiceNetwork ) { + qDebug() << "found service network" << c.name() << c.children().count(); + QVERIFY(c.isValid()); + QList members = c.children(); + foreach(QNetworkConfiguration child, members) { + QVERIFY(child.isValid()); + QVERIFY(configs.contains(child)); + qDebug() << "\t" << child.name(); + } + } + } +} + +void tst_QNetworkConfiguration::isRoamingAvailable() +{ + QNetworkConfigurationManager manager; + QList configs = manager.allConfigurations(); + + //force update to get maximum list + QSignalSpy spy(&manager, SIGNAL(updateCompleted())); + manager.updateConfigurations(); //initiate scans + QTRY_VERIFY(spy.count() == 1); //wait for scan to complete + + foreach(QNetworkConfiguration c, configs) + { + QVERIFY(QNetworkConfiguration::UserChoice != c.type()); + QVERIFY(QNetworkConfiguration::Invalid != c.type()); + if ( c.type() == QNetworkConfiguration::ServiceNetwork ) { + //cannot test flag as some SNAPs may not support roaming anyway + //QVERIFY(c.roamingavailable()) + if ( c.children().count() <= 1 ) + QVERIFY(!c.isRoamingAvailable()); + foreach(QNetworkConfiguration child, c.children()) { + QVERIFY(QNetworkConfiguration::InternetAccessPoint == child.type()); + QCOMPARE(child.children().count(), 0); + } + } else { + QVERIFY(!c.isRoamingAvailable()); + } + } +} + +QTEST_MAIN(tst_QNetworkConfiguration) +#include "tst_qnetworkconfiguration.moc" diff --git a/tests/auto/network/bearer/qnetworkconfigurationmanager/qnetworkconfigurationmanager.pro b/tests/auto/network/bearer/qnetworkconfigurationmanager/qnetworkconfigurationmanager.pro new file mode 100644 index 0000000000..d9c1d6bba6 --- /dev/null +++ b/tests/auto/network/bearer/qnetworkconfigurationmanager/qnetworkconfigurationmanager.pro @@ -0,0 +1,15 @@ +load(qttest_p4) +SOURCES += tst_qnetworkconfigurationmanager.cpp +HEADERS += ../qbearertestcommon.h + +QT = core network + +symbian { + TARGET.CAPABILITY = NetworkServices NetworkControl ReadUserData +} + +maemo6|maemo5 { + CONFIG += link_pkgconfig + + PKGCONFIG += conninet +} diff --git a/tests/auto/network/bearer/qnetworkconfigurationmanager/tst_qnetworkconfigurationmanager.cpp b/tests/auto/network/bearer/qnetworkconfigurationmanager/tst_qnetworkconfigurationmanager.cpp new file mode 100644 index 0000000000..d29ef77347 --- /dev/null +++ b/tests/auto/network/bearer/qnetworkconfigurationmanager/tst_qnetworkconfigurationmanager.cpp @@ -0,0 +1,378 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include "../qbearertestcommon.h" + +#include +#include + +#if defined(Q_OS_UNIX) && !defined(QT_NO_ICD) && !defined (Q_OS_SYMBIAN) +#include +#include +#endif + +QT_USE_NAMESPACE +class tst_QNetworkConfigurationManager : public QObject +{ + Q_OBJECT + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private slots: + void usedInThread(); // this test must be first, or it will falsely pass + void allConfigurations(); + void defaultConfiguration(); + void configurationFromIdentifier(); + +private: +#if defined(Q_OS_UNIX) && !defined(QT_NO_ICD) && !defined (Q_OS_SYMBIAN) + Maemo::IAPConf *iapconf; + Maemo::IAPConf *iapconf2; + Maemo::IAPConf *gprsiap; +#define MAX_IAPS 50 + Maemo::IAPConf *iaps[MAX_IAPS]; + QProcess *icd_stub; +#endif +}; + +void tst_QNetworkConfigurationManager::initTestCase() +{ +#if defined(Q_OS_UNIX) && !defined(QT_NO_ICD) && !defined (Q_OS_SYMBIAN) + iapconf = new Maemo::IAPConf("007"); + iapconf->setValue("ipv4_type", "AUTO"); + iapconf->setValue("wlan_wepkey1", "connt"); + iapconf->setValue("wlan_wepdefkey", 1); + iapconf->setValue("wlan_ssid", QByteArray("JamesBond")); + iapconf->setValue("name", "James Bond"); + iapconf->setValue("type", "WLAN_INFRA"); + + gprsiap = new Maemo::IAPConf("This-is-GPRS-IAP"); + gprsiap->setValue("ask_password", false); + gprsiap->setValue("gprs_accesspointname", "internet"); + gprsiap->setValue("gprs_password", ""); + gprsiap->setValue("gprs_username", ""); + gprsiap->setValue("ipv4_autodns", true); + gprsiap->setValue("ipv4_type", "AUTO"); + gprsiap->setValue("sim_imsi", "244070123456789"); + gprsiap->setValue("name", "MI6"); + gprsiap->setValue("type", "GPRS"); + + iapconf2 = new Maemo::IAPConf("osso.net"); + iapconf2->setValue("ipv4_type", "AUTO"); + iapconf2->setValue("wlan_wepkey1", "osso.net"); + iapconf2->setValue("wlan_wepdefkey", 1); + iapconf2->setValue("wlan_ssid", QByteArray("osso.net")); + iapconf2->setValue("name", "osso.net"); + iapconf2->setValue("type", "WLAN_INFRA"); + iapconf2->setValue("wlan_security", "WEP"); + + /* Create large number of IAPs in the gconf and see what happens */ + fflush(stdout); + printf("Creating %d IAPS: ", MAX_IAPS); + for (int i=0; isetValue("name", QString("test-iap-")+num); + iaps[i]->setValue("type", "WLAN_INFRA"); + iaps[i]->setValue("wlan_ssid", QString(QString("test-ssid-")+num).toAscii()); + iaps[i]->setValue("wlan_security", "WPA_PSK"); + iaps[i]->setValue("EAP_wpa_preshared_passphrase", QString("test-passphrase-")+num); + printf("."); + fflush(stdout); + } + printf("\n"); + fflush(stdout); + + icd_stub = new QProcess(this); + icd_stub->start("/usr/bin/icd2_stub.py"); + QTest::qWait(1000); + + // Add a known network to scan list that icd2 stub returns + QProcess dbus_send; + // 007 network + dbus_send.start("dbus-send --type=method_call --system " + "--dest=com.nokia.icd2 /com/nokia/icd2 " + "com.nokia.icd2.testing.add_available_network " + "string:'' uint32:0 string:'' " + "string:WLAN_INFRA uint32:5000011 array:byte:48,48,55"); + dbus_send.waitForFinished(); + + // osso.net network + dbus_send.start("dbus-send --type=method_call --system " + "--dest=com.nokia.icd2 /com/nokia/icd2 " + "com.nokia.icd2.testing.add_available_network " + "string:'' uint32:0 string:'' " + "string:WLAN_INFRA uint32:83886097 array:byte:111,115,115,111,46,110,101,116"); + dbus_send.waitForFinished(); +#endif +} + + +void tst_QNetworkConfigurationManager::cleanupTestCase() +{ +#if defined(Q_OS_UNIX) && !defined(QT_NO_ICD) && !defined (Q_OS_SYMBIAN) + iapconf->clear(); + delete iapconf; + iapconf2->clear(); + delete iapconf2; + gprsiap->clear(); + delete gprsiap; + + printf("Deleting %d IAPS : ", MAX_IAPS); + for (int i=0; iclear(); + delete iaps[i]; + printf("."); + fflush(stdout); + } + printf("\n"); + qDebug() << "Deleted" << MAX_IAPS << "IAPs"; + + icd_stub->terminate(); + icd_stub->waitForFinished(); +#endif +} + +void tst_QNetworkConfigurationManager::init() +{ +} + +void tst_QNetworkConfigurationManager::cleanup() +{ +} + +void printConfigurationDetails(const QNetworkConfiguration& p) +{ + qDebug() << p.name() <<": isvalid->" <"<< p.type() << + " roaming->" << p.isRoamingAvailable() << "identifier->" << p.identifier() << + " purpose->" << p.purpose() << " state->" << p.state(); +} + +void tst_QNetworkConfigurationManager::allConfigurations() +{ + QNetworkConfigurationManager manager; + QList preScanConfigs = manager.allConfigurations(); + + foreach(QNetworkConfiguration c, preScanConfigs) + { + QVERIFY2(c.type()!=QNetworkConfiguration::UserChoice, "allConfiguration must not return UserChoice configs"); + } + + QSignalSpy spy(&manager, SIGNAL(updateCompleted())); + manager.updateConfigurations(); //initiate scans + QTRY_VERIFY(spy.count() == 1); //wait for scan to complete + + QList configs = manager.allConfigurations(); + + int all = configs.count(); + qDebug() << "All configurations:" << all; + QVERIFY(all); + foreach(QNetworkConfiguration p, configs) { + QVERIFY(p.isValid()); + printConfigurationDetails(p); + QVERIFY(p.type() != QNetworkConfiguration::Invalid); + QVERIFY(p.type() != QNetworkConfiguration::UserChoice); + } + + configs = manager.allConfigurations(QNetworkConfiguration::Undefined); + int undefined = configs.count(); + QVERIFY(undefined <= all); + qDebug() << "Undefined configurations:" << undefined; + foreach( const QNetworkConfiguration p, configs) { + printConfigurationDetails(p); + QVERIFY(p.state() & QNetworkConfiguration::Undefined); + QVERIFY(!(p.state() & QNetworkConfiguration::Defined)); + } + + //get defined configs only (same as all) + configs = manager.allConfigurations(QNetworkConfiguration::Defined); + int defined = configs.count(); + qDebug() << "Defined configurations:" << defined; + QVERIFY(defined <= all); + foreach( const QNetworkConfiguration p, configs) { + printConfigurationDetails(p); + QVERIFY(p.state() & QNetworkConfiguration::Defined); + QVERIFY(!(p.state() & QNetworkConfiguration::Undefined)); + } + + //get discovered configurations only + configs = manager.allConfigurations(QNetworkConfiguration::Discovered); + int discovered = configs.count(); + //QVERIFY(discovered); + qDebug() << "Discovered configurations:" << discovered; + foreach(const QNetworkConfiguration p, configs) { + printConfigurationDetails(p); + QVERIFY(p.isValid()); + QVERIFY(!(p.state() & QNetworkConfiguration::Undefined)); + QVERIFY(p.state() & QNetworkConfiguration::Defined); + QVERIFY(p.state() & QNetworkConfiguration::Discovered); + } + + //getactive configurations only + configs = manager.allConfigurations(QNetworkConfiguration::Active); + int active = configs.count(); + if (active) + QVERIFY(manager.isOnline()); + else + QVERIFY(!manager.isOnline()); + + //QVERIFY(active); + qDebug() << "Active configurations:" << active; + foreach(const QNetworkConfiguration p, configs) { + printConfigurationDetails(p); + QVERIFY(p.isValid()); + QVERIFY(!(p.state() & QNetworkConfiguration::Undefined)); + QVERIFY(p.state() & QNetworkConfiguration::Active); + QVERIFY(p.state() & QNetworkConfiguration::Discovered); + QVERIFY(p.state() & QNetworkConfiguration::Defined); + } + + QVERIFY(all >= discovered); + QVERIFY(discovered >= active); +} + + +void tst_QNetworkConfigurationManager::defaultConfiguration() +{ + QNetworkConfigurationManager manager; + QSignalSpy spy(&manager, SIGNAL(updateCompleted())); + manager.updateConfigurations(); //initiate scans + QTRY_VERIFY(spy.count() == 1); //wait for scan to complete + + QList configs = manager.allConfigurations(); + QNetworkConfiguration defaultConfig = manager.defaultConfiguration(); + + bool confirm = configs.contains(defaultConfig); + + if (defaultConfig.type() != QNetworkConfiguration::UserChoice) { + QVERIFY(confirm || !defaultConfig.isValid()); + QVERIFY(!(confirm && !defaultConfig.isValid())); + } else { + QVERIFY(!confirm); // user choice config is not part of allConfigurations() + QVERIFY(defaultConfig.isValid()); + QCOMPARE(defaultConfig.name(), QString("UserChoice")); + QCOMPARE(defaultConfig.children().count(), 0); + QVERIFY(!defaultConfig.isRoamingAvailable()); + QCOMPARE(defaultConfig.state(), QNetworkConfiguration::Discovered); + QNetworkConfiguration copy = manager.configurationFromIdentifier(defaultConfig.identifier()); + QVERIFY(copy == defaultConfig); + } +} + +void tst_QNetworkConfigurationManager::configurationFromIdentifier() +{ + QNetworkConfigurationManager manager; + QSet allIdentifier; + + //force an update to get maximum number of configs + QSignalSpy spy(&manager, SIGNAL(updateCompleted())); + manager.updateConfigurations(); //initiate scans + QTRY_VERIFY(spy.count() == 1); //wait for scan to complete + + QList configs = manager.allConfigurations(); + + foreach(QNetworkConfiguration c, configs) { + QVERIFY(!allIdentifier.contains(c.identifier())); + allIdentifier.insert(c.identifier()); + + QNetworkConfiguration direct = manager.configurationFromIdentifier(c.identifier()); + QVERIFY(direct.isValid()); + QVERIFY(direct == c); + } + + //assume that there is no item with identifier 'FooBar' + QVERIFY(!allIdentifier.contains("FooBar")); + QNetworkConfiguration invalid = manager.configurationFromIdentifier("FooBar"); + QVERIFY(!invalid.isValid()); +} + +class QNCMTestThread : public QThread +{ +protected: + virtual void run() + { + QNetworkConfigurationManager manager; + preScanConfigs = manager.allConfigurations(); + QSignalSpy spy(&manager, SIGNAL(updateCompleted())); + manager.updateConfigurations(); //initiate scans + QTRY_VERIFY(spy.count() == 1); //wait for scan to complete + configs = manager.allConfigurations(); + } +public: + QList configs; + QList preScanConfigs; +}; + +// regression test for QTBUG-18795 +void tst_QNetworkConfigurationManager::usedInThread() +{ +#if defined Q_OS_MAC && !defined (QT_NO_COREWLAN) + QSKIP("QTBUG-19070 Mac CoreWlan plugin is broken", SkipAll); +#else + QNCMTestThread thread; + connect(&thread, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + thread.start(); + QTestEventLoop::instance().enterLoop(100); //QTRY_VERIFY could take ~90 seconds to time out in the thread + QVERIFY(!QTestEventLoop::instance().timeout()); + qDebug() << "prescan:" << thread.preScanConfigs.count(); + qDebug() << "postscan:" << thread.configs.count(); + + QNetworkConfigurationManager manager; + QList preScanConfigs = manager.allConfigurations(); + QSignalSpy spy(&manager, SIGNAL(updateCompleted())); + manager.updateConfigurations(); //initiate scans + QTRY_VERIFY(spy.count() == 1); //wait for scan to complete + QList configs = manager.allConfigurations(); + QCOMPARE(thread.configs, configs); + //Don't compare pre scan configs, because these may be cached and therefore give different results + //which makes the test unstable. The post scan results should have all configurations every time + //QCOMPARE(thread.preScanConfigs, preScanConfigs); +#endif +} + +QTEST_MAIN(tst_QNetworkConfigurationManager) +#include "tst_qnetworkconfigurationmanager.moc" diff --git a/tests/auto/network/bearer/qnetworksession/lackey/lackey.pro b/tests/auto/network/bearer/qnetworksession/lackey/lackey.pro new file mode 100644 index 0000000000..5db6743c65 --- /dev/null +++ b/tests/auto/network/bearer/qnetworksession/lackey/lackey.pro @@ -0,0 +1,15 @@ +SOURCES += main.cpp +TARGET = lackey + +QT = core network + +DESTDIR = ./ + +win32:CONFIG += console +mac:CONFIG -= app_bundle + +symbian { + # Needed for interprocess communication and opening QNetworkSession + TARGET.CAPABILITY = NetworkControl NetworkServices +} + diff --git a/tests/auto/network/bearer/qnetworksession/lackey/main.cpp b/tests/auto/network/bearer/qnetworksession/lackey/main.cpp new file mode 100644 index 0000000000..73c52cdc8d --- /dev/null +++ b/tests/auto/network/bearer/qnetworksession/lackey/main.cpp @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +QT_USE_NAMESPACE + + +#define NO_DISCOVERED_CONFIGURATIONS_ERROR 1 +#define SESSION_OPEN_ERROR 2 + + +int main(int argc, char** argv) +{ + QCoreApplication app(argc, argv); + + // Update configurations so that everything is up to date for this process too. + // Event loop is used to wait for awhile. + QNetworkConfigurationManager manager; + manager.updateConfigurations(); + QEventLoop iIgnoreEventLoop; + QTimer::singleShot(3000, &iIgnoreEventLoop, SLOT(quit())); + iIgnoreEventLoop.exec(); + + QList discovered = + manager.allConfigurations(QNetworkConfiguration::Discovered); + + foreach(QNetworkConfiguration config, discovered) { + qDebug() << "Lackey: Name of the config enumerated: " << config.name(); + qDebug() << "Lackey: State of the config enumerated: " << config.state(); + } + + if (discovered.isEmpty()) { + qDebug("Lackey: no discovered configurations, returning empty error."); + return NO_DISCOVERED_CONFIGURATIONS_ERROR; + } + + // Cannot read/write to processes on WinCE or Symbian. + // Easiest alternative is to use sockets for IPC. + QLocalSocket oopSocket; + + oopSocket.connectToServer("tst_qnetworksession"); + oopSocket.waitForConnected(-1); + + qDebug() << "Lackey started"; + + QNetworkSession *session = 0; + do { + if (session) { + delete session; + session = 0; + } + + qDebug() << "Discovered configurations:" << discovered.count(); + + if (discovered.isEmpty()) { + qDebug() << "No more discovered configurations"; + break; + } + + qDebug() << "Taking first configuration"; + + QNetworkConfiguration config = discovered.takeFirst(); + + if ((config.state() & QNetworkConfiguration::Active) == QNetworkConfiguration::Active) { + qDebug() << config.name() << "is active, therefore skipping it (looking for configs in 'discovered' state)."; + continue; + } + + qDebug() << "Creating session for" << config.name() << config.identifier(); + + session = new QNetworkSession(config); + + QString output = QString("Starting session for %1\n").arg(config.identifier()); + oopSocket.write(output.toAscii()); + oopSocket.waitForBytesWritten(); + session->open(); + session->waitForOpened(); + } while (!(session && session->isOpen())); + + qDebug() << "lackey: loop done"; + + if (!session) { + qDebug() << "Could not start session"; + + oopSocket.disconnectFromServer(); + oopSocket.waitForDisconnected(-1); + + return SESSION_OPEN_ERROR; + } + + QString output = QString("Started session for %1\n").arg(session->configuration().identifier()); + oopSocket.write(output.toAscii()); + oopSocket.waitForBytesWritten(); + + oopSocket.waitForReadyRead(); + oopSocket.readLine(); + + session->stop(); + + delete session; + + oopSocket.disconnectFromServer(); + oopSocket.waitForDisconnected(-1); + + return 0; +} diff --git a/tests/auto/network/bearer/qnetworksession/qnetworksession.pro b/tests/auto/network/bearer/qnetworksession/qnetworksession.pro new file mode 100644 index 0000000000..34761e53cf --- /dev/null +++ b/tests/auto/network/bearer/qnetworksession/qnetworksession.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS = lackey test +symbian: TARGET.CAPABILITY = NetworkServices diff --git a/tests/auto/network/bearer/qnetworksession/test/test.pro b/tests/auto/network/bearer/qnetworksession/test/test.pro new file mode 100644 index 0000000000..bfffe907ec --- /dev/null +++ b/tests/auto/network/bearer/qnetworksession/test/test.pro @@ -0,0 +1,26 @@ +load(qttest_p4) +SOURCES += tst_qnetworksession.cpp +HEADERS += ../../qbearertestcommon.h + +QT = core network + +TARGET = tst_qnetworksession +CONFIG(debug_and_release) { + CONFIG(debug, debug|release) { + DESTDIR = ../debug + } else { + DESTDIR = ../release + } +} else { + DESTDIR = .. +} + +symbian { + TARGET.CAPABILITY = NetworkServices NetworkControl ReadUserData PowerMgmt +} + +maemo6|maemo5 { + CONFIG += link_pkgconfig + + PKGCONFIG += conninet +} diff --git a/tests/auto/network/bearer/qnetworksession/test/tst_qnetworksession.cpp b/tests/auto/network/bearer/qnetworksession/test/tst_qnetworksession.cpp new file mode 100644 index 0000000000..0c35a552f6 --- /dev/null +++ b/tests/auto/network/bearer/qnetworksession/test/tst_qnetworksession.cpp @@ -0,0 +1,1683 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include "../../qbearertestcommon.h" + +#include +#include + +#if defined(Q_OS_UNIX) && !defined(QT_NO_ICD) && !defined (Q_OS_SYMBIAN) +#include +#include +#endif + +QT_USE_NAMESPACE + +// Can be used to configure tests that require manual attention (such as roaming) +//#define QNETWORKSESSION_MANUAL_TESTS 1 + +Q_DECLARE_METATYPE(QNetworkConfiguration) +Q_DECLARE_METATYPE(QNetworkConfiguration::Type); + +class tst_QNetworkSession : public QObject +{ + Q_OBJECT + +public slots: + void initTestCase(); + void cleanupTestCase(); + +private slots: + + void robustnessBombing(); + + void sessionClosing_data(); + void sessionClosing(); + + void outOfProcessSession(); + void invalidSession(); + + void repeatedOpenClose_data(); + void repeatedOpenClose(); + + void sessionStop_data(); + void sessionStop(); + + void roamingErrorCodes(); + + void sessionProperties_data(); + void sessionProperties(); + + void userChoiceSession_data(); + void userChoiceSession(); + + void sessionOpenCloseStop_data(); + void sessionOpenCloseStop(); + + void sessionAutoClose_data(); + void sessionAutoClose(); + +private: + QNetworkConfigurationManager manager; + QMap testsToRun; + + int inProcessSessionManagementCount; + +#if defined(Q_OS_UNIX) && !defined(QT_NO_ICD) && !defined (Q_OS_SYMBIAN) + Maemo::IAPConf *iapconf; + Maemo::IAPConf *iapconf2; + Maemo::IAPConf *gprsiap; +#define MAX_IAPS 10 + Maemo::IAPConf *iaps[MAX_IAPS]; + QProcess *icd_stub; +#endif +}; + +// Helper functions +bool openSession(QNetworkSession *session); +bool closeSession(QNetworkSession *session, bool lastSessionOnConfiguration = true); +void updateConfigurations(); +void printConfigurations(); +QNetworkConfiguration suitableConfiguration(QString bearerType, QNetworkConfiguration::Type configType); + +void tst_QNetworkSession::initTestCase() +{ + qRegisterMetaType("QNetworkConfiguration"); + qRegisterMetaType("QNetworkConfiguration::Type"); + + // If you wish to skip tests, set value as false. This is often very convinient because tests are so lengthy. + // Better way still would be to make this readable from a file. + testsToRun["robustnessBombing"] = true; + testsToRun["sessionClosing"] = true; + testsToRun["outOfProcessSession"] = true; + testsToRun["invalidSession"] = true; + testsToRun["repeatedOpenClose"] = true; + testsToRun["roamingErrorCodes"] = true; + testsToRun["sessionStop"] = true; + testsToRun["sessionProperties"] = true; + testsToRun["userChoiceSession"] = true; + testsToRun["sessionOpenCloseStop"] = true; + +#if defined(Q_OS_UNIX) && !defined(QT_NO_ICD) && !defined (Q_OS_SYMBIAN) + iapconf = new Maemo::IAPConf("007"); + iapconf->setValue("ipv4_type", "AUTO"); + iapconf->setValue("wlan_wepkey1", "connt"); + iapconf->setValue("wlan_wepdefkey", 1); + iapconf->setValue("wlan_ssid", QByteArray("JamesBond")); + iapconf->setValue("name", "James Bond"); + iapconf->setValue("type", "WLAN_INFRA"); + + gprsiap = new Maemo::IAPConf("This-is-GPRS-IAP"); + gprsiap->setValue("ask_password", false); + gprsiap->setValue("gprs_accesspointname", "internet"); + gprsiap->setValue("gprs_password", ""); + gprsiap->setValue("gprs_username", ""); + gprsiap->setValue("ipv4_autodns", true); + gprsiap->setValue("ipv4_type", "AUTO"); + gprsiap->setValue("sim_imsi", "244070123456789"); + gprsiap->setValue("name", "MI6"); + gprsiap->setValue("type", "GPRS"); + + iapconf2 = new Maemo::IAPConf("osso.net"); + iapconf2->setValue("ipv4_type", "AUTO"); + iapconf2->setValue("wlan_wepkey1", "osso.net"); + iapconf2->setValue("wlan_wepdefkey", 1); + iapconf2->setValue("wlan_ssid", QByteArray("osso.net")); + iapconf2->setValue("name", "osso.net"); + iapconf2->setValue("type", "WLAN_INFRA"); + iapconf2->setValue("wlan_security", "WEP"); + + /* Create large number of IAPs in the gconf and see what happens */ + fflush(stdout); + printf("Creating %d IAPS: ", MAX_IAPS); + for (int i=0; isetValue("name", QString("test-iap-")+num); + iaps[i]->setValue("type", "WLAN_INFRA"); + iaps[i]->setValue("wlan_ssid", QString(QString("test-ssid-")+num).toAscii()); + iaps[i]->setValue("wlan_security", "WPA_PSK"); + iaps[i]->setValue("EAP_wpa_preshared_passphrase", QString("test-passphrase-")+num); + printf("."); + fflush(stdout); + } + printf("\n"); + fflush(stdout); + + icd_stub = new QProcess(this); + icd_stub->start("/usr/bin/icd2_stub.py"); + QTest::qWait(1000); + + // Add a known network to scan list that icd2 stub returns + QProcess dbus_send; + // 007 network + dbus_send.start("dbus-send --type=method_call --system " + "--dest=com.nokia.icd2 /com/nokia/icd2 " + "com.nokia.icd2.testing.add_available_network " + "string:'' uint32:0 string:'' " + "string:WLAN_INFRA uint32:5000011 array:byte:48,48,55"); + dbus_send.waitForFinished(); + + // osso.net network + dbus_send.start("dbus-send --type=method_call --system " + "--dest=com.nokia.icd2 /com/nokia/icd2 " + "com.nokia.icd2.testing.add_available_network " + "string:'' uint32:0 string:'' " + "string:WLAN_INFRA uint32:83886097 array:byte:111,115,115,111,46,110,101,116"); + dbus_send.waitForFinished(); +#endif + + inProcessSessionManagementCount = -1; + + QSignalSpy spy(&manager, SIGNAL(updateCompleted())); + manager.updateConfigurations(); + QTRY_VERIFY(spy.count() == 1); +} + +void tst_QNetworkSession::cleanupTestCase() +{ + if (!(manager.capabilities() & QNetworkConfigurationManager::SystemSessionSupport) && + (manager.capabilities() & QNetworkConfigurationManager::CanStartAndStopInterfaces) && + inProcessSessionManagementCount == 0) { + qWarning("No usable configurations found to complete all possible tests in " + "inProcessSessionManagement()"); + } + +#if defined(Q_OS_UNIX) && !defined(QT_NO_ICD) && !defined (Q_OS_SYMBIAN) + iapconf->clear(); + delete iapconf; + iapconf2->clear(); + delete iapconf2; + gprsiap->clear(); + delete gprsiap; + + printf("Deleting %d IAPS : ", MAX_IAPS); + for (int i=0; iclear(); + delete iaps[i]; + printf("."); + fflush(stdout); + } + printf("\n"); + qDebug() << "Deleted" << MAX_IAPS << "IAPs"; + + icd_stub->terminate(); + icd_stub->waitForFinished(); +#endif +} + +// Robustness test for calling interfaces in nonsense order / with nonsense parameters +void tst_QNetworkSession::robustnessBombing() +{ + if (!testsToRun["robustnessBombing"]) { + QSKIP("Temporary skip due to value set false (or it is missing) in testsToRun map", SkipAll); + } + + QNetworkConfigurationManager mgr; + QNetworkSession testSession(mgr.defaultConfiguration()); + // Should not reset even session is not opened + testSession.migrate(); + testSession.accept(); + testSession.ignore(); + testSession.reject(); +} + +void tst_QNetworkSession::sessionClosing_data() { + QTest::addColumn("bearerType"); + QTest::addColumn("configurationType"); + + QTest::newRow("WLAN_IAP") << "WLAN" << QNetworkConfiguration::InternetAccessPoint; + QTest::newRow("Cellular_IAP") << "cellular" << QNetworkConfiguration::InternetAccessPoint; + QTest::newRow("SNAP") << "bearer_type_not_relevant_with_SNAPs" << QNetworkConfiguration::ServiceNetwork; +} + +// Testcase for closing the session at unexpected times +void tst_QNetworkSession::sessionClosing() +{ + if (!testsToRun["sessionClosing"]) { + QSKIP("Temporary skip due to value set false (or it is missing) in testsToRun map", SkipAll); + } + QFETCH(QString, bearerType); + QFETCH(QNetworkConfiguration::Type, configurationType); + + // Update configurations so that WLANs are discovered too. + updateConfigurations(); + + // First check that opening once succeeds and determine if test is doable + QNetworkConfiguration config = suitableConfiguration(bearerType, configurationType); + if (!config.isValid()) { + QSKIP("No suitable configurations, skipping this round of repeated open-close test.", SkipSingle); + } + qDebug() << "Using following configuration to bomb with close(): " << config.name(); + QNetworkSession session(config); + if (!openSession(&session) || + !closeSession(&session)) { + QSKIP("Unable to open/close session, skipping this round of close() bombing.", SkipSingle); + } + + qDebug() << "Closing without issuing open()"; + session.close(); + + for (int i = 0; i < 25; i++) { + qDebug() << "Opening and then waiting: " << i * 100 << " ms before closing."; + session.open(); + QTest::qWait(i*100); + session.close(); + // Sooner or later session must end in Disconnected state, + // no matter what the phase was. + QTRY_VERIFY(session.state() == QNetworkSession::Disconnected); + QTest::qWait(200); // Give platform a breathe, otherwise we'll be catching other errors + } +} + +void tst_QNetworkSession::invalidSession() +{ + if (!testsToRun["invalidSession"]) { + QSKIP("Temporary skip due to value set false (or it is missing) in testsToRun map", SkipAll); + } + // 1. Verify that session created with invalid configuration remains in invalid state + QNetworkSession session(QNetworkConfiguration(), 0); + QVERIFY(!session.isOpen()); + QVERIFY(session.state() == QNetworkSession::Invalid); + QVERIFY(session.error() == QNetworkSession::InvalidConfigurationError); + + // 2. Verify that opening session with invalid configuration both 1) emits invalidconfigurationerror and 2) sets session's state as invalid. + QSignalSpy errorSpy(&session, SIGNAL(error(QNetworkSession::SessionError))); + session.open(); + session.waitForOpened(1000); // Should bail out right away + QVERIFY(errorSpy.count() == 1); + QNetworkSession::SessionError error = + qvariant_cast (errorSpy.first().at(0)); + QVERIFY(error == QNetworkSession::InvalidConfigurationError); + QVERIFY(session.error() == QNetworkSession::InvalidConfigurationError); + QVERIFY(session.state() == QNetworkSession::Invalid); + +#ifdef QNETWORKSESSION_MANUAL_TESTS + + QNetworkConfiguration invalidatedConfig = suitableConfiguration("WLAN",QNetworkConfiguration::InternetAccessPoint); + if (invalidatedConfig.isValid()) { + // 3. Verify that invalidating a session after its successfully configured works + QNetworkSession invalidatedSession(invalidatedConfig); + qDebug() << "Delete the WLAN IAP from phone now (waiting 60 seconds): " << invalidatedConfig.name(); + QTest::qWait(60000); + QVERIFY(!invalidatedConfig.isValid()); + QVERIFY(invalidatedSession.state() == QNetworkSession::Invalid); + qDebug() << "Add the WLAN IAP back (waiting 60 seconds): " << invalidatedConfig.name(); + QTest::qWait(60000); + } + + QNetworkConfiguration definedConfig = suitableConfiguration("WLAN",QNetworkConfiguration::InternetAccessPoint); + if (definedConfig.isValid()) { + // 4. Verify that opening a session with defined configuration emits error and enters notavailable-state + // TODO these timer waits should be changed to waiting appropriate signals, now these wait excessively + qDebug() << "Shutdown WLAN IAP (waiting 60 seconds): " << definedConfig.name(); + QTest::qWait(60000); + // Shutting down WLAN should bring back to defined -state. + QVERIFY((definedConfig.state() & QNetworkConfiguration::Defined) == QNetworkConfiguration::Defined); + QNetworkSession definedSession(definedConfig); + QSignalSpy errorSpy(&definedSession, SIGNAL(error(QNetworkSession::SessionError))); + QNetworkSession::SessionError sessionError; + updateConfigurations(); + + definedSession.open(); +#ifdef Q_OS_SYMBIAN + // On symbian, the connection opening is tried even with defined state. + qDebug("Waiting for 10 seconds to all signals to propagate."); + QTest::qWait(10000); +#endif + updateConfigurations(); + + QVERIFY(definedConfig.isValid()); // Session remains valid + QVERIFY(definedSession.state() == QNetworkSession::NotAvailable); // State is not available because WLAN is not in coverage + QVERIFY(!errorSpy.isEmpty()); // Session tells with error about invalidated configuration + sessionError = qvariant_cast (errorSpy.first().at(0)); + QVERIFY(sessionError == QNetworkSession::InvalidConfigurationError); + qDebug() << "Turn the WLAN IAP back on (waiting 60 seconds): " << definedConfig.name(); + QTest::qWait(60000); + updateConfigurations(); + QVERIFY(definedConfig.state() == QNetworkConfiguration::Discovered); + } + +#endif +} + +void tst_QNetworkSession::sessionProperties_data() +{ + QTest::addColumn("configuration"); + + QTest::newRow("invalid configuration") << QNetworkConfiguration(); + + foreach (const QNetworkConfiguration &config, manager.allConfigurations()) { + const QString name = config.name().isEmpty() ? QString("") : config.name(); + QTest::newRow(name.toLocal8Bit().constData()) << config; + } +} + +void tst_QNetworkSession::sessionProperties() +{ + if (!testsToRun["sessionProperties"]) { + QSKIP("Temporary skip due to value set false (or it is missing) in testsToRun map", SkipAll); + } + QFETCH(QNetworkConfiguration, configuration); + QNetworkSession session(configuration); + QVERIFY(session.configuration() == configuration); + QStringList validBearerNames = QStringList() << QLatin1String("Unknown") + << QLatin1String("Ethernet") + << QLatin1String("WLAN") + << QLatin1String("2G") + << QLatin1String("CDMA2000") + << QLatin1String("WCDMA") + << QLatin1String("HSPA") + << QLatin1String("Bluetooth") + << QLatin1String("WiMAX"); + + if (!configuration.isValid()) { + QVERIFY(configuration.bearerName().isEmpty()); + } else { + switch (configuration.type()) + { + case QNetworkConfiguration::ServiceNetwork: + case QNetworkConfiguration::UserChoice: + default: + QVERIFY(configuration.bearerName().isEmpty()); + break; + case QNetworkConfiguration::InternetAccessPoint: + QVERIFY(validBearerNames.contains(configuration.bearerName())); + break; + } + } + + // QNetworkSession::interface() should return an invalid interface unless + // session is in the connected state. +#ifndef QT_NO_NETWORKINTERFACE +#if !(defined(Q_OS_SYMBIAN) && defined(__WINS__)) + // On Symbian emulator, the support for data bearers is limited + QCOMPARE(session.state() == QNetworkSession::Connected, session.interface().isValid()); +#endif +#endif + + if (!configuration.isValid()) { + QVERIFY(configuration.state() == QNetworkConfiguration::Undefined && + session.state() == QNetworkSession::Invalid); + } else { + switch (configuration.state()) { + case QNetworkConfiguration::Undefined: + QVERIFY(session.state() == QNetworkSession::NotAvailable); + break; + case QNetworkConfiguration::Defined: + QVERIFY(session.state() == QNetworkSession::NotAvailable); + break; + case QNetworkConfiguration::Discovered: + QVERIFY(session.state() == QNetworkSession::Connecting || + session.state() == QNetworkSession::Disconnected); + break; + case QNetworkConfiguration::Active: + QVERIFY(session.state() == QNetworkSession::Connected || + session.state() == QNetworkSession::Closing || + session.state() == QNetworkSession::Roaming); + break; + default: + QFAIL("Invalid configuration state"); + }; + } +} + +void tst_QNetworkSession::repeatedOpenClose_data() { + QTest::addColumn("bearerType"); + QTest::addColumn("configurationType"); + QTest::addColumn("repeatTimes"); + + QTest::newRow("WLAN_IAP") << "WLAN" << QNetworkConfiguration::InternetAccessPoint << 3; + // QTest::newRow("Cellular_IAP") << "cellular" << QNetworkConfiguration::InternetAccessPoint << 3; + // QTest::newRow("SNAP") << "bearer_type_not_relevant_with_SNAPs" << QNetworkConfiguration::ServiceNetwork << 3; +} + +// Tests repeated-open close. +void tst_QNetworkSession::repeatedOpenClose() +{ + if (!testsToRun["repeatedOpenClose"]) { + QSKIP("Temporary skip due to value set false (or it is missing) in testsToRun map", SkipAll); + } + + QFETCH(QString, bearerType); + QFETCH(QNetworkConfiguration::Type, configurationType); + QFETCH(int, repeatTimes); + + // First check that opening once succeeds and determine if repeatable testing is doable + QNetworkConfiguration config = suitableConfiguration(bearerType, configurationType); + if (!config.isValid()) { + QSKIP("No suitable configurations, skipping this round of repeated open-close test.", SkipSingle); + } + qDebug() << "Using following configuratio to repeatedly open and close: " << config.name(); + QNetworkSession permanentSession(config); + if (!openSession(&permanentSession) || + !closeSession(&permanentSession)) { + QSKIP("Unable to open/close session, skipping this round of repeated open-close test.", SkipSingle); + } + for (int i = 0; i < repeatTimes; i++) { + qDebug() << "Opening, loop number " << i; + QVERIFY(openSession(&permanentSession)); + qDebug() << "Closing, loop number, then waiting 5 seconds: " << i; + QVERIFY(closeSession(&permanentSession)); + QTest::qWait(5000); + } +} + +void tst_QNetworkSession::roamingErrorCodes() +{ + if (!testsToRun["roamingErrorCodes"]) { + QSKIP("Temporary skip due to value set false (or it is missing) in testsToRun map", SkipAll); + } +#ifndef Q_OS_SYMBIAN + QSKIP("Roaming supported on Symbian.", SkipAll); +#else + QNetworkConfiguration wlanIapConfig = suitableConfiguration("WLAN", QNetworkConfiguration::InternetAccessPoint); + if (!wlanIapConfig.isValid()) { + QSKIP("No WLAN IAP accessible, skipping test.", SkipAll); + } + // Check that opening and closing two sessions on same config work gracefully: + QNetworkSession iapSession(wlanIapConfig); + QVERIFY(openSession(&iapSession)); + QNetworkSession adminIapSession(wlanIapConfig); + QVERIFY(openSession(&adminIapSession)); + QVERIFY(closeSession(&iapSession, false)); // false == not a last session based on the configuration + QVERIFY(closeSession(&adminIapSession)); + + // Open configurations again, force close bearer and check that errors are emitted correctly + // on the other session + QVERIFY(openSession(&iapSession)); + QVERIFY(openSession(&adminIapSession)); + QSignalSpy errorSpy(&iapSession, SIGNAL(error(QNetworkSession::SessionError))); + adminIapSession.stop(); // requires NetworkControl capabilities + QTRY_VERIFY(!errorSpy.isEmpty()); // wait for error signals + QNetworkSession::SessionError error = qvariant_cast(errorSpy.first().at(0)); + QTest::qWait(2000); // Wait for a moment to all platform signals to propagate + QVERIFY(error == QNetworkSession::SessionAbortedError); + QVERIFY(iapSession.state() == QNetworkSession::Disconnected); + QVERIFY(adminIapSession.state() == QNetworkSession::Disconnected); +#endif // Q_OS_SYMBIAN +} + + +void tst_QNetworkSession::sessionStop_data() { + QTest::addColumn("bearerType"); + QTest::addColumn("configurationType"); + + QTest::newRow("SNAP") << "bearer_type_not_relevant_with_SNAPs" << QNetworkConfiguration::ServiceNetwork; + QTest::newRow("WLAN_IAP") << "WLAN" << QNetworkConfiguration::InternetAccessPoint; + QTest::newRow("Cellular_IAP") << "cellular" << QNetworkConfiguration::InternetAccessPoint; +} + +void tst_QNetworkSession::sessionStop() +{ + if (!testsToRun["sessionStop"]) { + QSKIP("Temporary skip due to value set false (or it is missing) in testsToRun map", SkipAll); + } +#ifndef Q_OS_SYMBIAN + QSKIP("Testcase contains mainly Symbian specific checks, because it is only platform to really support interface (IAP-level) Stop.", SkipAll); +#endif + QFETCH(QString, bearerType); + QFETCH(QNetworkConfiguration::Type, configurationType); + + int configWaitdelayInMs = 2000; + + updateConfigurations(); + printConfigurations(); + + QNetworkConfiguration config = suitableConfiguration(bearerType, configurationType); + if (!config.isValid()) { + QSKIP("No suitable configurations, skipping this round of session stop test.", SkipSingle); + } + qDebug() << "Using following configuration to open and stop a session: " << config.name(); + + QNetworkSession openedSession(config); + QNetworkSession closedSession(config); + QNetworkSession innocentSession(config); + QNetworkConfigurationManager mgr; + + QSignalSpy closedSessionOpenedSpy(&closedSession, SIGNAL(opened())); + QSignalSpy closedSessionClosedSpy(&closedSession, SIGNAL(closed())); + QSignalSpy closedSessionStateChangedSpy(&closedSession, SIGNAL(stateChanged(QNetworkSession::State))); + QSignalSpy closedErrorSpy(&closedSession, SIGNAL(error(QNetworkSession::SessionError))); + + QSignalSpy openedSessionClosedSpy(&openedSession, SIGNAL(closed())); + QSignalSpy openedSessionStateChangedSpy(&openedSession, SIGNAL(stateChanged(QNetworkSession::State))); + + QSignalSpy innocentSessionClosedSpy(&innocentSession, SIGNAL(closed())); + QSignalSpy innocentSessionStateChangedSpy(&innocentSession, SIGNAL(stateChanged(QNetworkSession::State))); + QSignalSpy innocentErrorSpy(&innocentSession, SIGNAL(error(QNetworkSession::SessionError))); + QNetworkSession::SessionError sessionError; + + // 1. Verify that stopping an opened session works (the simplest usecase). + qDebug("----------1. Verify that stopping an opened session works (the simplest usecase)"); + QSignalSpy configChangeSpy(&mgr, SIGNAL(configurationChanged(QNetworkConfiguration))); + QVERIFY(openSession(&openedSession)); + qDebug("Waiting for %d ms to get all configurationChange signals from platform.", configWaitdelayInMs); + // Clear signals caused by opening + closedSessionOpenedSpy.clear(); + closedSessionClosedSpy.clear(); + closedSessionStateChangedSpy.clear(); + closedErrorSpy.clear(); + openedSessionStateChangedSpy.clear(); + openedSessionClosedSpy.clear(); + + openedSession.stop(); + + qDebug("Waiting for %d ms to get all configurationChange signals from platform.", configWaitdelayInMs); + QTest::qWait(configWaitdelayInMs); // Wait to get all relevant configurationChange() signals + + // First to closing, then to disconnected + QVERIFY(openedSessionStateChangedSpy.count() == 2); + QVERIFY(!openedSessionClosedSpy.isEmpty()); + QVERIFY(openedSession.state() == QNetworkSession::Disconnected); + QVERIFY(config.state() != QNetworkConfiguration::Active); + + // 2. Verify that stopping a session based on non-connected configuration does nothing + qDebug("----------2. Verify that stopping a session based on non-connected configuration does nothing"); + QNetworkSession::State closedSessionOriginalState = closedSession.state(); + // Clear all possible signals + configChangeSpy.clear(); + closedSessionOpenedSpy.clear(); + closedSessionClosedSpy.clear(); + closedSessionStateChangedSpy.clear(); + closedErrorSpy.clear(); + + closedSession.stop(); + qDebug("Waiting for %d ms to get all configurationChange signals from platform.", configWaitdelayInMs); + QTest::qWait(configWaitdelayInMs); // Wait to get all relevant configurationChange() signals + + QVERIFY(closedSessionOpenedSpy.isEmpty()); + QVERIFY(closedSessionClosedSpy.isEmpty()); + QVERIFY(closedSessionStateChangedSpy.isEmpty()); + QVERIFY(closedErrorSpy.isEmpty()); + QVERIFY(closedSession.state() == closedSessionOriginalState); // State remains + + // 3. Check that stopping a opened session affects also other opened session based on the same configuration. + if (config.type() == QNetworkConfiguration::InternetAccessPoint) { + qDebug("----------3. Check that stopping a opened session affects also other opened session based on the same configuration."); + + QVERIFY(openSession(&openedSession)); + QVERIFY(openSession(&innocentSession)); + + configChangeSpy.clear(); + innocentSessionClosedSpy.clear(); + innocentSessionStateChangedSpy.clear(); + innocentErrorSpy.clear(); + + openedSession.stop(); + qDebug("Waiting for %d ms to get all configurationChange signals from platform.", configWaitdelayInMs); + QTest::qWait(configWaitdelayInMs); // Wait to get all relevant configurationChange() signals + QTest::qWait(configWaitdelayInMs); // Wait to get all relevant configurationChange() signals + + QVERIFY(!innocentSessionClosedSpy.isEmpty()); + QVERIFY(!innocentSessionStateChangedSpy.isEmpty()); + QVERIFY(!innocentErrorSpy.isEmpty()); + QVERIFY(innocentSession.state() == QNetworkSession::Disconnected); + QVERIFY(openedSession.state() == QNetworkSession::Disconnected); + sessionError = qvariant_cast(innocentErrorSpy.first().at(0)); + QVERIFY(sessionError == QNetworkSession::SessionAbortedError); + + innocentSessionClosedSpy.clear(); + innocentSessionStateChangedSpy.clear(); + innocentErrorSpy.clear(); + } else { + qDebug("----------3. Skip for SNAP configuration."); + } + // 4. Check that stopping a non-opened session stops the other session based on the + // same configuration if configuration is IAP. Stopping closed SNAP session has no impact on other opened SNAP session. + if (config.type() == QNetworkConfiguration::ServiceNetwork) { + qDebug("----------4. Skip for SNAP configuration."); + } else if (config.type() == QNetworkConfiguration::InternetAccessPoint) { + qDebug("----------4. Check that stopping a non-opened session stops the other session based on the same configuration"); + qDebug("----------4.1 Opening innocent session"); + QVERIFY(openSession(&innocentSession)); + qDebug("Waiting for %d ms after open to make sure all platform indications are propagated", configWaitdelayInMs); + QTest::qWait(configWaitdelayInMs); + qDebug("----------4.2 Calling closedSession.stop()"); + closedSession.stop(); + qDebug("Waiting for %d ms to get all configurationChange signals from platform..", configWaitdelayInMs); + QTest::qWait(configWaitdelayInMs); // Wait to get all relevant configurationChange() signals + + QTest::qWait(configWaitdelayInMs); + QTest::qWait(configWaitdelayInMs); + + QVERIFY(!innocentSessionClosedSpy.isEmpty()); + QVERIFY(!innocentSessionStateChangedSpy.isEmpty()); + QVERIFY(!innocentErrorSpy.isEmpty()); + QVERIFY(innocentSession.state() == QNetworkSession::Disconnected); + QVERIFY(closedSession.state() == QNetworkSession::Disconnected); + sessionError = qvariant_cast(innocentErrorSpy.first().at(0)); + QVERIFY(sessionError == QNetworkSession::SessionAbortedError); + QVERIFY(config.state() == QNetworkConfiguration::Discovered); + } + + // 5. Sanity check that stopping invalid session does not crash + qDebug("----------5. Sanity check that stopping invalid session does not crash"); + QNetworkSession invalidSession(QNetworkConfiguration(), 0); + QVERIFY(invalidSession.state() == QNetworkSession::Invalid); + invalidSession.stop(); + QVERIFY(invalidSession.state() == QNetworkSession::Invalid); +} + +void tst_QNetworkSession::userChoiceSession_data() +{ + QTest::addColumn("configuration"); + + QNetworkConfiguration config = manager.defaultConfiguration(); + if (config.type() == QNetworkConfiguration::UserChoice) + QTest::newRow("UserChoice") << config; + else + QSKIP("Default configuration is not a UserChoice configuration.", SkipAll); +} + +void tst_QNetworkSession::userChoiceSession() +{ + if (!testsToRun["userChoiceSession"]) { + QSKIP("Temporary skip due to value set false (or it is missing) in testsToRun map", SkipAll); + } + QFETCH(QNetworkConfiguration, configuration); + + QVERIFY(configuration.type() == QNetworkConfiguration::UserChoice); + + QNetworkSession session(configuration); + + // Check that configuration was really set + QVERIFY(session.configuration() == configuration); + + QVERIFY(!session.isOpen()); + + // Check that session is not active + QVERIFY(session.sessionProperty("ActiveConfiguration").toString().isEmpty()); + + // The remaining tests require the session to be not NotAvailable. + if (session.state() == QNetworkSession::NotAvailable) + QSKIP("Network is not available.", SkipSingle); + + QSignalSpy sessionOpenedSpy(&session, SIGNAL(opened())); + QSignalSpy sessionClosedSpy(&session, SIGNAL(closed())); + QSignalSpy stateChangedSpy(&session, SIGNAL(stateChanged(QNetworkSession::State))); + QSignalSpy errorSpy(&session, SIGNAL(error(QNetworkSession::SessionError))); + + // Test opening the session. + { + bool expectStateChange = session.state() != QNetworkSession::Connected; + + session.open(); + +#if defined(Q_OS_SYMBIAN) + // Opening & closing multiple connections in a row sometimes + // results hanging of connection opening on Symbian devices + // => If first open fails, wait a moment and try again. + if (!session.waitForOpened()) { + qDebug("**** Session open Timeout - Wait 5 seconds and try once again ****"); + session.close(); + QTest::qWait(5000); // Wait a while before trying to open session again + session.open(); + session.waitForOpened(); + } +#else + session.waitForOpened(); +#endif + + if (session.isOpen()) + QVERIFY(!sessionOpenedSpy.isEmpty() || !errorSpy.isEmpty()); + if (!errorSpy.isEmpty()) { + QNetworkSession::SessionError error = + qvariant_cast(errorSpy.first().at(0)); + if (error == QNetworkSession::OperationNotSupportedError) { + // The session needed to bring up the interface, + // but the operation is not supported. + QSKIP("Configuration does not support open().", SkipSingle); + } else if (error == QNetworkSession::InvalidConfigurationError) { + // The session needed to bring up the interface, but it is not possible for the + // specified configuration. + if ((session.configuration().state() & QNetworkConfiguration::Discovered) == + QNetworkConfiguration::Discovered) { + QFAIL("Failed to open session for Discovered configuration."); + } else { + QSKIP("Cannot test session for non-Discovered configuration.", SkipSingle); + } + } else if (error == QNetworkSession::UnknownSessionError) { + QSKIP("Unknown session error.", SkipSingle); + } else { + QFAIL("Error opening session."); + } + } else if (!sessionOpenedSpy.isEmpty()) { + QCOMPARE(sessionOpenedSpy.count(), 1); + QVERIFY(sessionClosedSpy.isEmpty()); + QVERIFY(errorSpy.isEmpty()); + + if (expectStateChange) + QTRY_VERIFY(!stateChangedSpy.isEmpty()); + + QVERIFY(session.state() == QNetworkSession::Connected); +#ifndef QT_NO_NETWORKINTERFACE +#if !(defined(Q_OS_SYMBIAN) && defined(__WINS__)) + // On Symbian emulator, the support for data bearers is limited + QVERIFY(session.interface().isValid()); +#endif +#endif + + const QString userChoiceIdentifier = + session.sessionProperty("UserChoiceConfiguration").toString(); + + QVERIFY(!userChoiceIdentifier.isEmpty()); + QVERIFY(userChoiceIdentifier != configuration.identifier()); + + QNetworkConfiguration userChoiceConfiguration = + manager.configurationFromIdentifier(userChoiceIdentifier); + + QVERIFY(userChoiceConfiguration.isValid()); + QVERIFY(userChoiceConfiguration.type() != QNetworkConfiguration::UserChoice); + + const QString testIdentifier("abc"); + //resetting UserChoiceConfiguration is ignored (read only property) + session.setSessionProperty("UserChoiceConfiguration", testIdentifier); + QVERIFY(session.sessionProperty("UserChoiceConfiguration").toString() != testIdentifier); + + const QString activeIdentifier = + session.sessionProperty("ActiveConfiguration").toString(); + + QVERIFY(!activeIdentifier.isEmpty()); + QVERIFY(activeIdentifier != configuration.identifier()); + + QNetworkConfiguration activeConfiguration = + manager.configurationFromIdentifier(activeIdentifier); + + QVERIFY(activeConfiguration.isValid()); + QVERIFY(activeConfiguration.type() == QNetworkConfiguration::InternetAccessPoint); + + //resetting ActiveConfiguration is ignored (read only property) + session.setSessionProperty("ActiveConfiguration", testIdentifier); + QVERIFY(session.sessionProperty("ActiveConfiguration").toString() != testIdentifier); + + if (userChoiceConfiguration.type() == QNetworkConfiguration::InternetAccessPoint) { + QVERIFY(userChoiceConfiguration == activeConfiguration); + } else { + QVERIFY(userChoiceConfiguration.type() == QNetworkConfiguration::ServiceNetwork); + QVERIFY(userChoiceConfiguration.children().contains(activeConfiguration)); + } + } else { + QFAIL("Timeout waiting for session to open."); + } + } +} + +void tst_QNetworkSession::sessionOpenCloseStop_data() +{ + QTest::addColumn("configuration"); + QTest::addColumn("forceSessionStop"); + + foreach (const QNetworkConfiguration &config, manager.allConfigurations()) { + const QString name = config.name().isEmpty() ? QString("") : config.name(); + QTest::newRow((name + QLatin1String(" close")).toLocal8Bit().constData()) + << config << false; + QTest::newRow((name + QLatin1String(" stop")).toLocal8Bit().constData()) + << config << true; + } + + inProcessSessionManagementCount = 0; +} + +void tst_QNetworkSession::sessionOpenCloseStop() +{ + if (!testsToRun["sessionOpenCloseStop"]) { + QSKIP("Temporary skip due to value set false (or it is missing) in testsToRun map", SkipAll); + } + QFETCH(QNetworkConfiguration, configuration); + QFETCH(bool, forceSessionStop); + + QNetworkSession session(configuration); + + // Test initial state of the session. + { + QVERIFY(session.configuration() == configuration); + QVERIFY(!session.isOpen()); + // session may be invalid if configuration is removed between when + // sessionOpenCloseStop_data() is called and here. + QVERIFY((configuration.isValid() && (session.state() != QNetworkSession::Invalid)) || + (!configuration.isValid() && (session.state() == QNetworkSession::Invalid))); + QVERIFY(session.error() == QNetworkSession::UnknownSessionError); + } + + // The remaining tests require the session to be not NotAvailable. + if (session.state() == QNetworkSession::NotAvailable) + QSKIP("Network is not available.", SkipSingle); + + QSignalSpy sessionOpenedSpy(&session, SIGNAL(opened())); + QSignalSpy sessionClosedSpy(&session, SIGNAL(closed())); + QSignalSpy stateChangedSpy(&session, SIGNAL(stateChanged(QNetworkSession::State))); + QSignalSpy errorSpy(&session, SIGNAL(error(QNetworkSession::SessionError))); + + // Test opening the session. + { + QNetworkSession::State previousState = session.state(); + bool expectStateChange = previousState != QNetworkSession::Connected; + + session.open(); + +#if defined(Q_OS_SYMBIAN) + // Opening & closing multiple connections in a row sometimes + // results hanging of connection opening on Symbian devices + // => If first open fails, wait a moment and try again. + if (!session.waitForOpened()) { + qDebug("**** Session open Timeout - Wait 5 seconds and try once again ****"); + session.close(); + QTest::qWait(5000); // Wait a while before trying to open session again + session.open(); + session.waitForOpened(); + } +#else + session.waitForOpened(); +#endif + + // Wait until the configuration is uptodate as well, it may be signaled 'connected' + // bit later than the session + QTRY_VERIFY(configuration.state() == QNetworkConfiguration::Active); + + if (session.isOpen()) + QVERIFY(!sessionOpenedSpy.isEmpty() || !errorSpy.isEmpty()); + if (!errorSpy.isEmpty()) { + QNetworkSession::SessionError error = + qvariant_cast(errorSpy.first().at(0)); + + QVERIFY(session.state() == previousState); + + if (error == QNetworkSession::OperationNotSupportedError) { + // The session needed to bring up the interface, + // but the operation is not supported. + QSKIP("Configuration does not support open().", SkipSingle); + } else if (error == QNetworkSession::InvalidConfigurationError) { + // The session needed to bring up the interface, but it is not possible for the + // specified configuration. + if ((session.configuration().state() & QNetworkConfiguration::Discovered) == + QNetworkConfiguration::Discovered) { + QFAIL("Failed to open session for Discovered configuration."); + } else { + QSKIP("Cannot test session for non-Discovered configuration.", SkipSingle); + } + } else if (error == QNetworkSession::UnknownSessionError) { + QSKIP("Unknown Session error.", SkipSingle); + } else { + QFAIL("Error opening session."); + } + } else if (!sessionOpenedSpy.isEmpty()) { + QCOMPARE(sessionOpenedSpy.count(), 1); + QVERIFY(sessionClosedSpy.isEmpty()); + QVERIFY(errorSpy.isEmpty()); + + if (expectStateChange) { + QTRY_VERIFY(stateChangedSpy.count() >= 2); + + QNetworkSession::State state = + qvariant_cast(stateChangedSpy.at(0).at(0)); + QVERIFY(state == QNetworkSession::Connecting); + + state = qvariant_cast(stateChangedSpy.at(1).at(0)); + QVERIFY(state == QNetworkSession::Connected); + } + + QVERIFY(session.state() == QNetworkSession::Connected); +#ifndef QT_NO_NETWORKINTERFACE +#if !(defined(Q_OS_SYMBIAN) && defined(__WINS__)) + // On Symbian emulator, the support for data bearers is limited + QVERIFY(session.interface().isValid()); +#endif +#endif + } else { + QFAIL("Timeout waiting for session to open."); + } + } + + sessionOpenedSpy.clear(); + sessionClosedSpy.clear(); + stateChangedSpy.clear(); + errorSpy.clear(); + + QNetworkSession session2(configuration); + + QSignalSpy sessionOpenedSpy2(&session2, SIGNAL(opened())); + QSignalSpy sessionClosedSpy2(&session2, SIGNAL(closed())); + QSignalSpy stateChangedSpy2(&session2, SIGNAL(stateChanged(QNetworkSession::State))); + QSignalSpy errorSpy2(&session2, SIGNAL(error(QNetworkSession::SessionError))); + + // Test opening a second session. + { + QVERIFY(session2.configuration() == configuration); + QVERIFY(!session2.isOpen()); + QVERIFY(session2.state() == QNetworkSession::Connected); + QVERIFY(session.error() == QNetworkSession::UnknownSessionError); + + session2.open(); + + QTRY_VERIFY(!sessionOpenedSpy2.isEmpty() || !errorSpy2.isEmpty()); + + if (errorSpy2.isEmpty()) { + QVERIFY(session2.isOpen()); + QVERIFY(session2.state() == QNetworkSession::Connected); + } + QVERIFY(session.isOpen()); + QVERIFY(session.state() == QNetworkSession::Connected); +#ifndef QT_NO_NETWORKINTERFACE +#if !(defined(Q_OS_SYMBIAN) && defined(__WINS__)) + // On Symbian emulator, the support for data bearers is limited + QVERIFY(session.interface().isValid()); +#endif + if (errorSpy2.isEmpty()) { + QCOMPARE(session.interface().hardwareAddress(), session2.interface().hardwareAddress()); + QCOMPARE(session.interface().index(), session2.interface().index()); + } +#endif + } + + sessionOpenedSpy2.clear(); + + if (forceSessionStop && session2.isOpen()) { + // Test forcing the second session to stop the interface. + QNetworkSession::State previousState = session.state(); +#ifdef Q_CC_NOKIAX86 + // For S60 emulator builds: RConnection::Stop does not work on all Emulators + bool expectStateChange = false; +#else + bool expectStateChange = previousState != QNetworkSession::Disconnected; +#endif + session2.stop(); + + // QNetworkSession::stop() must result either closed() signal + // or error() signal + QTRY_VERIFY(!sessionClosedSpy2.isEmpty() || !errorSpy2.isEmpty()); + QVERIFY(!session2.isOpen()); + + if (!errorSpy2.isEmpty()) { + // QNetworkSession::stop() resulted error() signal for session2 + // => also session should emit error() signal + QTRY_VERIFY(!errorSpy.isEmpty()); + + // check for SessionAbortedError + QNetworkSession::SessionError error = + qvariant_cast(errorSpy.first().at(0)); + QNetworkSession::SessionError error2 = + qvariant_cast(errorSpy2.first().at(0)); + + QVERIFY(error == QNetworkSession::SessionAbortedError); + QVERIFY(error2 == QNetworkSession::SessionAbortedError); + + QCOMPARE(errorSpy.count(), 1); + QCOMPARE(errorSpy2.count(), 1); + + errorSpy.clear(); + errorSpy2.clear(); + } + + QVERIFY(errorSpy.isEmpty()); + QVERIFY(errorSpy2.isEmpty()); + + // Wait for Disconnected state + QTRY_NOOP(session2.state() == QNetworkSession::Disconnected); + + if (expectStateChange) + QTRY_VERIFY(stateChangedSpy2.count() >= 1 || !errorSpy2.isEmpty()); + + if (!errorSpy2.isEmpty()) { + QVERIFY(session2.state() == previousState); + QVERIFY(session.state() == previousState); + + QNetworkSession::SessionError error = + qvariant_cast(errorSpy2.first().at(0)); + if (error == QNetworkSession::OperationNotSupportedError) { + // The session needed to bring down the interface, + // but the operation is not supported. + QSKIP("Configuration does not support stop().", SkipSingle); + } else if (error == QNetworkSession::InvalidConfigurationError) { + // The session needed to bring down the interface, but it is not possible for the + // specified configuration. + if ((session.configuration().state() & QNetworkConfiguration::Discovered) == + QNetworkConfiguration::Discovered) { + QFAIL("Failed to stop session for Discovered configuration."); + } else { + QSKIP("Cannot test session for non-Discovered configuration.", SkipSingle); + } + } else { + QFAIL("Error stopping session."); + } + } else if (!sessionClosedSpy2.isEmpty()) { + if (expectStateChange) { + if (configuration.type() == QNetworkConfiguration::ServiceNetwork) { + bool roamedSuccessfully = false; + + QNetworkSession::State state; + if (stateChangedSpy2.count() == 4) { + state = qvariant_cast(stateChangedSpy2.at(0).at(0)); + QVERIFY(state == QNetworkSession::Connecting); + + state = qvariant_cast(stateChangedSpy2.at(1).at(0)); + QVERIFY(state == QNetworkSession::Connected); + + state = qvariant_cast(stateChangedSpy2.at(2).at(0)); + QVERIFY(state == QNetworkSession::Closing); + + state = qvariant_cast(stateChangedSpy2.at(3).at(0)); + QVERIFY(state == QNetworkSession::Disconnected); + } else if (stateChangedSpy2.count() == 2) { + state = qvariant_cast(stateChangedSpy2.at(0).at(0)); + QVERIFY(state == QNetworkSession::Closing); + + state = qvariant_cast(stateChangedSpy2.at(1).at(0)); + QVERIFY(state == QNetworkSession::Disconnected); + } else { + QFAIL("Unexpected amount of state changes when roaming."); + } + + QTRY_VERIFY(session.state() == QNetworkSession::Roaming || + session.state() == QNetworkSession::Connected || + session.state() == QNetworkSession::Disconnected); + + QTRY_VERIFY(stateChangedSpy.count() > 0); + state = qvariant_cast(stateChangedSpy.at(stateChangedSpy.count() - 1).at(0)); + + for (int i = 0; i < stateChangedSpy.count(); i++) { + QNetworkSession::State state_temp = + qvariant_cast(stateChangedSpy.at(i).at(0)); + // Extra debug because a fragile point in testcase because statuses vary. + qDebug() << "------- Statechange spy at: " << i << " is " << state_temp; + } + + if (state == QNetworkSession::Roaming) { + QTRY_VERIFY(session.state() == QNetworkSession::Connected); + QTRY_VERIFY(session2.state() == QNetworkSession::Connected); + roamedSuccessfully = true; + } else if (state == QNetworkSession::Closing) { + QTRY_VERIFY(session2.state() == QNetworkSession::Disconnected); + QTRY_VERIFY(session.state() == QNetworkSession::Connected || + session.state() == QNetworkSession::Disconnected); + roamedSuccessfully = false; + } else if (state == QNetworkSession::Disconnected) { + QTRY_VERIFY(!errorSpy.isEmpty()); + QTRY_VERIFY(session2.state() == QNetworkSession::Disconnected); + } else if (state == QNetworkSession::Connected) { + QTRY_VERIFY(errorSpy.isEmpty()); + + if (stateChangedSpy.count() > 1) { + state = qvariant_cast(stateChangedSpy.at(stateChangedSpy.count() - 2).at(0)); + QVERIFY(state == QNetworkSession::Roaming); + } + roamedSuccessfully = true; + } + + if (roamedSuccessfully) { + // Verify that you can open session based on the disconnected configuration + QString configId = session.sessionProperty("ActiveConfiguration").toString(); + QNetworkConfiguration config = manager.configurationFromIdentifier(configId); + QNetworkSession session3(config); + QSignalSpy errorSpy3(&session3, SIGNAL(error(QNetworkSession::SessionError))); + QSignalSpy sessionOpenedSpy3(&session3, SIGNAL(opened())); + session3.open(); + session3.waitForOpened(); + QTest::qWait(1000); // Wait awhile to get all signals from platform + if (session.isOpen()) + QVERIFY(!sessionOpenedSpy3.isEmpty() || !errorSpy3.isEmpty()); + session.stop(); + QTRY_VERIFY(session.state() == QNetworkSession::Disconnected); + } +#ifndef Q_CC_NOKIAX86 + if (!roamedSuccessfully) + QVERIFY(!errorSpy.isEmpty()); +#endif + } else { + QTest::qWait(2000); // Wait awhile to get all signals from platform + + if (stateChangedSpy2.count() == 2) { + QNetworkSession::State state = + qvariant_cast(stateChangedSpy2.at(0).at(0)); + QVERIFY(state == QNetworkSession::Closing); + state = qvariant_cast(stateChangedSpy2.at(1).at(0)); + QVERIFY(state == QNetworkSession::Disconnected); + } else { + QVERIFY(stateChangedSpy2.count() >= 1); + + for (int i = 0; i < stateChangedSpy2.count(); i++) { + QNetworkSession::State state_temp = + qvariant_cast(stateChangedSpy2.at(i).at(0)); + // Extra debug because a fragile point in testcase. + qDebug() << "+++++ Statechange spy at: " << i << " is " << state_temp; + } + + QNetworkSession::State state = + qvariant_cast(stateChangedSpy2.at(stateChangedSpy2.count() - 1).at(0)); + // Symbian version dependant. + QVERIFY(state == QNetworkSession::Disconnected); + } + } + + QTRY_VERIFY(!sessionClosedSpy.isEmpty()); + QTRY_VERIFY(session.state() == QNetworkSession::Disconnected); + } + + QVERIFY(errorSpy2.isEmpty()); + + ++inProcessSessionManagementCount; + } else { + QFAIL("Timeout waiting for session to stop."); + } + +#ifndef Q_CC_NOKIAX86 + QVERIFY(!sessionClosedSpy.isEmpty()); +#endif + QVERIFY(!sessionClosedSpy2.isEmpty()); + +#ifndef Q_CC_NOKIAX86 + QVERIFY(!session.isOpen()); +#endif + QVERIFY(!session2.isOpen()); + } else if (session2.isOpen()) { + // Test closing the second session. + { + int stateChangedCountBeforeClose = stateChangedSpy2.count(); + session2.close(); + + QTRY_VERIFY(!sessionClosedSpy2.isEmpty()); +#ifndef Q_CC_NOKIAX86 + QVERIFY(stateChangedSpy2.count() == stateChangedCountBeforeClose); +#endif + + QVERIFY(sessionClosedSpy.isEmpty()); + + QVERIFY(session.isOpen()); + QVERIFY(!session2.isOpen()); + QVERIFY(session.state() == QNetworkSession::Connected); + QVERIFY(session2.state() == QNetworkSession::Connected); +#ifndef QT_NO_NETWORKINTERFACE +#if !(defined(Q_OS_SYMBIAN) && defined(__WINS__)) + // On Symbian emulator, the support for data bearers is limited + QVERIFY(session.interface().isValid()); +#endif + QCOMPARE(session.interface().hardwareAddress(), session2.interface().hardwareAddress()); + QCOMPARE(session.interface().index(), session2.interface().index()); +#endif + } + + sessionClosedSpy2.clear(); + + // Test closing the first session. + { +#ifdef Q_CC_NOKIAX86 + // For S60 emulator builds: RConnection::Close does not actually + // close network connection on all Emulators + bool expectStateChange = false; +#else + bool expectStateChange = session.state() != QNetworkSession::Disconnected && + manager.capabilities() & QNetworkConfigurationManager::SystemSessionSupport; +#endif + + session.close(); + + QTRY_VERIFY(!sessionClosedSpy.isEmpty() || !errorSpy.isEmpty()); + + QVERIFY(!session.isOpen()); + + if (expectStateChange) + QTRY_VERIFY(!stateChangedSpy.isEmpty() || !errorSpy.isEmpty()); + + if (!errorSpy.isEmpty()) { + QNetworkSession::SessionError error = + qvariant_cast(errorSpy.first().at(0)); + if (error == QNetworkSession::OperationNotSupportedError) { + // The session needed to bring down the interface, + // but the operation is not supported. + QSKIP("Configuration does not support close().", SkipSingle); + } else if (error == QNetworkSession::InvalidConfigurationError) { + // The session needed to bring down the interface, but it is not possible for the + // specified configuration. + if ((session.configuration().state() & QNetworkConfiguration::Discovered) == + QNetworkConfiguration::Discovered) { + QFAIL("Failed to close session for Discovered configuration."); + } else { + QSKIP("Cannot test session for non-Discovered configuration.", SkipSingle); + } + } else { + QFAIL("Error closing session."); + } + } else if (!sessionClosedSpy.isEmpty()) { + QVERIFY(sessionOpenedSpy.isEmpty()); + QCOMPARE(sessionClosedSpy.count(), 1); + if (expectStateChange) + QVERIFY(!stateChangedSpy.isEmpty()); + QVERIFY(errorSpy.isEmpty()); + + if (expectStateChange) + QTRY_VERIFY(session.state() == QNetworkSession::Disconnected); + + ++inProcessSessionManagementCount; + } else { + QFAIL("Timeout waiting for session to close."); + } + } + } +} + +QDebug operator<<(QDebug debug, const QList &list) +{ + debug.nospace() << "( "; + foreach (const QNetworkConfiguration &config, list) + debug.nospace() << config.identifier() << ", "; + debug.nospace() << ")\n"; + return debug; +} + +// Note: outOfProcessSession requires that at least one configuration is +// at Discovered -state. +void tst_QNetworkSession::outOfProcessSession() +{ + if (!testsToRun["outOfProcessSession"]) { + QSKIP("Temporary skip due to value set false (or it is missing) in testsToRun map", SkipAll); + } +#if defined(Q_OS_SYMBIAN) && defined(__WINS__) + QSKIP("Symbian emulator does not support two [QR]PRocesses linking a dll (QtBearer.dll) with global writeable static data.", SkipAll); +#endif + updateConfigurations(); + QTest::qWait(2000); + + QNetworkConfigurationManager manager; + // Create a QNetworkConfigurationManager to detect configuration changes made in Lackey. This + // is actually the essence of this testcase - to check that platform mediates/reflects changes + // regardless of process boundaries. The interprocess communication is more like a way to get + // this test-case act correctly and timely. + QList before = manager.allConfigurations(QNetworkConfiguration::Active); + QSignalSpy spy(&manager, SIGNAL(configurationChanged(QNetworkConfiguration))); + + // Cannot read/write to processes on WinCE or Symbian. + // Easiest alternative is to use sockets for IPC. + QLocalServer oopServer; + // First remove possible earlier listening address which would cause listen to fail + // (e.g. previously abruptly ended unit test might cause this) + QLocalServer::removeServer("tst_qnetworksession"); + oopServer.listen("tst_qnetworksession"); + + QProcess lackey; + lackey.start("lackey/lackey"); + qDebug() << lackey.error() << lackey.errorString(); + QVERIFY(lackey.waitForStarted()); + + + QVERIFY(oopServer.waitForNewConnection(-1)); + QLocalSocket *oopSocket = oopServer.nextPendingConnection(); + + do { + QByteArray output; + + if (oopSocket->waitForReadyRead()) + output = oopSocket->readLine().trimmed(); + + if (output.startsWith("Started session ")) { + QString identifier = QString::fromLocal8Bit(output.mid(20).constData()); + QNetworkConfiguration changed; + + do { + QTRY_VERIFY(!spy.isEmpty()); + changed = qvariant_cast(spy.takeFirst().at(0)); + } while (changed.identifier() != identifier); + + QVERIFY((changed.state() & QNetworkConfiguration::Active) == + QNetworkConfiguration::Active); + + QVERIFY(!before.contains(changed)); + + QList after = + manager.allConfigurations(QNetworkConfiguration::Active); + + QVERIFY(after.contains(changed)); + + spy.clear(); + + oopSocket->write("stop\n"); + oopSocket->waitForBytesWritten(); + + do { + QTRY_VERIFY(!spy.isEmpty()); + + changed = qvariant_cast(spy.takeFirst().at(0)); + } while (changed.identifier() != identifier); + + QVERIFY((changed.state() & QNetworkConfiguration::Active) != + QNetworkConfiguration::Active); + + QList afterStop = + manager.allConfigurations(QNetworkConfiguration::Active); + + QVERIFY(!afterStop.contains(changed)); + + oopSocket->disconnectFromServer(); + oopSocket->waitForDisconnected(-1); + + lackey.waitForFinished(); + } + // This is effected by QTBUG-4903, process will always report as running + //} while (lackey.state() == QProcess::Running); + + // Workaround: the socket in the lackey will disconnect on exit + } while (oopSocket->state() == QLocalSocket::ConnectedState); + + switch (lackey.exitCode()) { + case 0: + qDebug("Lackey returned exit success (0)"); + break; + case 1: + QSKIP("No discovered configurations found.", SkipAll); + case 2: + QSKIP("Lackey could not start session.", SkipAll); + default: + QSKIP("Lackey failed", SkipAll); + } +} + +// A convenience / helper function for testcases. Return the first matching configuration. +// Ignores configurations in other than 'discovered' -state. Returns invalid (QNetworkConfiguration()) +// if none found. +QNetworkConfiguration suitableConfiguration(QString bearerType, QNetworkConfiguration::Type configType) { + + // Refresh configurations and derive configurations matching given parameters. + QNetworkConfigurationManager mgr; + QSignalSpy updateSpy(&mgr, SIGNAL(updateCompleted())); + + mgr.updateConfigurations(); + QTRY_NOOP(updateSpy.count() == 1); + if (updateSpy.count() != 1) { + qDebug("tst_QNetworkSession::suitableConfiguration() failure: unable to update configurations"); + return QNetworkConfiguration(); + } + QList discoveredConfigs = mgr.allConfigurations(QNetworkConfiguration::Discovered); + foreach(QNetworkConfiguration config, discoveredConfigs) { + if ((config.state() & QNetworkConfiguration::Active) == QNetworkConfiguration::Active) { + discoveredConfigs.removeOne(config); + } else if (config.type() != configType) { + // qDebug() << "Dumping config because type (IAP/SNAP) mismatches: " << config.name(); + discoveredConfigs.removeOne(config); + } else if ((config.type() == QNetworkConfiguration::InternetAccessPoint) && + bearerType == "cellular") { // 'cellular' bearertype is for convenience + if (config.bearerName() != "2G" && + config.bearerName() != "CDMA2000" && + config.bearerName() != "WCDMA" && + config.bearerName() != "HSPA") { + // qDebug() << "Dumping config because bearer mismatches (cellular): " << config.name(); + discoveredConfigs.removeOne(config); + } + } else if ((config.type() == QNetworkConfiguration::InternetAccessPoint) && + bearerType != config.bearerName()) { + // qDebug() << "Dumping config because bearer mismatches (WLAN): " << config.name(); + discoveredConfigs.removeOne(config); + } + } + if (discoveredConfigs.isEmpty()) { + qDebug("tst_QNetworkSession::suitableConfiguration() failure: no suitable configurations present."); + return QNetworkConfiguration(); + } else { + return discoveredConfigs.first(); + } +} + +// A convenience-function: updates configurations and waits that they are updated. +void updateConfigurations() +{ + QNetworkConfigurationManager mgr; + QSignalSpy updateSpy(&mgr, SIGNAL(updateCompleted())); + mgr.updateConfigurations(); + QTRY_NOOP(updateSpy.count() == 1); +} + +// A convenience-function: updates and prints all available confiurations and their states +void printConfigurations() +{ + QNetworkConfigurationManager manager; + QList allConfigs = + manager.allConfigurations(); + qDebug("tst_QNetworkSession::printConfigurations QNetworkConfigurationManager gives following configurations: "); + foreach(QNetworkConfiguration config, allConfigs) { + qDebug() << "Name of the configuration: " << config.name(); + qDebug() << "State of the configuration: " << config.state(); + } +} + +// A convenience function for test-cases: opens the given configuration and return +// true if it was done gracefully. +bool openSession(QNetworkSession *session) { + bool result = true; + QNetworkConfigurationManager mgr; + QSignalSpy openedSpy(session, SIGNAL(opened())); + QSignalSpy stateChangeSpy(session, SIGNAL(stateChanged(QNetworkSession::State))); + QSignalSpy errorSpy(session, SIGNAL(error(QNetworkSession::SessionError))); + QSignalSpy configChangeSpy(&mgr, SIGNAL(configurationChanged(QNetworkConfiguration))); + // Store some initial statuses, because expected signals differ if the config is already + // active by some other session + QNetworkConfiguration::StateFlags configInitState = session->configuration().state(); + QNetworkSession::State sessionInitState = session->state(); + qDebug() << "tst_QNetworkSession::openSession() name of the configuration to be opened: " << session->configuration().name(); + qDebug() << "tst_QNetworkSession::openSession() state of the configuration to be opened: " << session->configuration().state(); + qDebug() << "tst_QNetworkSession::openSession() state of the session to be opened: " << session->state(); + + if (session->isOpen() || + !session->sessionProperty("ActiveConfiguration").toString().isEmpty()) { + qDebug("tst_QNetworkSession::openSession() failure: session was already open / active."); + result = false; + } else { + session->open(); + session->waitForOpened(120000); // Bringing interfaces up and down may take time at platform + } + QTest::qWait(5000); // Wait a moment to ensure all signals are propagated + // Check that connection opening went by the book. Add checks here if more strictness needed. + if (!session->isOpen()) { + qDebug("tst_QNetworkSession::openSession() failure: QNetworkSession::open() failed."); + result = false; + } + if (openedSpy.count() != 1) { + qDebug("tst_QNetworkSession::openSession() failure: QNetworkSession::opened() - signal not received."); + result = false; + } + if (!errorSpy.isEmpty()) { + qDebug("tst_QNetworkSession::openSession() failure: QNetworkSession::error() - signal was detected."); + result = false; + } + if (sessionInitState != QNetworkSession::Connected && + stateChangeSpy.isEmpty()) { + qDebug("tst_QNetworkSession::openSession() failure: QNetworkSession::stateChanged() - signals not detected."); + result = false; + } + if (configInitState != QNetworkConfiguration::Active && + configChangeSpy.isEmpty()) { + qDebug("tst_QNetworkSession::openSession() failure: QNetworkConfigurationManager::configurationChanged() - signals not detected."); + result = false; + } + if (session->configuration().state() != QNetworkConfiguration::Active) { + qDebug("tst_QNetworkSession::openSession() failure: session's configuration is not in 'Active' -state."); + qDebug() << "tst_QNetworkSession::openSession() state is: " << session->configuration().state(); + result = false; + } + if (result == false) { + qDebug() << "tst_QNetworkSession::openSession() opening session failed."; + } else { + qDebug() << "tst_QNetworkSession::openSession() opening session succeeded."; + } + qDebug() << "tst_QNetworkSession::openSession() name of the configuration is: " << session->configuration().name(); + qDebug() << "tst_QNetworkSession::openSession() configuration state is: " << session->configuration().state(); + qDebug() << "tst_QNetworkSession::openSession() session state is: " << session->state(); + + return result; +} + +// Helper function for closing opened session. Performs checks that +// session is closed gradefully (e.g. signals). Function does not delete +// the session. The lastSessionOnConfiguration (true by default) is used to +// tell if there are more sessions open, basing on same configuration. This +// impacts the checks made. +bool closeSession(QNetworkSession *session, bool lastSessionOnConfiguration) { + if (!session) { + qDebug("tst_QNetworkSession::closeSession() failure: NULL session given"); + return false; + } + + qDebug() << "tst_QNetworkSession::closeSession() name of the configuration to be closed: " << session->configuration().name(); + qDebug() << "tst_QNetworkSession::closeSession() state of the configuration to be closed: " << session->configuration().state(); + qDebug() << "tst_QNetworkSession::closeSession() state of the session to be closed: " << session->state(); + + if (session->state() != QNetworkSession::Connected || + !session->isOpen()) { + qDebug("tst_QNetworkSession::closeSession() failure: session is not opened."); + return false; + } + QNetworkConfigurationManager mgr; + QSignalSpy sessionClosedSpy(session, SIGNAL(closed())); + QSignalSpy sessionStateChangedSpy(session, SIGNAL(stateChanged(QNetworkSession::State))); + QSignalSpy sessionErrorSpy(session, SIGNAL(error(QNetworkSession::SessionError))); + QSignalSpy configChangeSpy(&mgr, SIGNAL(configurationChanged(QNetworkConfiguration))); + + bool result = true; + session->close(); + QTest::qWait(5000); // Wait a moment so that all signals are propagated + + if (!sessionErrorSpy.isEmpty()) { + qDebug("tst_QNetworkSession::closeSession() failure: QNetworkSession::error() received."); + result = false; + } + if (sessionClosedSpy.count() != 1) { + qDebug("tst_QNetworkSession::closeSession() failure: QNetworkSession::closed() signal not received."); + result = false; + } + if (lastSessionOnConfiguration && + sessionStateChangedSpy.isEmpty()) { + qDebug("tst_QNetworkSession::closeSession() failure: QNetworkSession::stateChanged() signals not received."); + result = false; + } + if (lastSessionOnConfiguration && + session->state() != QNetworkSession::Disconnected) { + qDebug("tst_QNetworkSession::closeSession() failure: QNetworkSession is not in Disconnected -state"); + result = false; + } + QTRY_NOOP(!configChangeSpy.isEmpty()); + if (lastSessionOnConfiguration && + configChangeSpy.isEmpty()) { + qDebug("tst_QNetworkSession::closeSession() failure: QNetworkConfigurationManager::configurationChanged() - signal not detected."); + result = false; + } + if (lastSessionOnConfiguration && + session->configuration().state() == QNetworkConfiguration::Active) { + qDebug("tst_QNetworkSession::closeSession() failure: session's configuration is still in active state."); + result = false; + } + if (result == false) { + qDebug() << "tst_QNetworkSession::closeSession() closing session failed."; + } else { + qDebug() << "tst_QNetworkSession::closeSession() closing session succeeded."; + } + qDebug() << "tst_QNetworkSession::closeSession() name of the configuration is: " << session->configuration().name(); + qDebug() << "tst_QNetworkSession::closeSession() configuration state is: " << session->configuration().state(); + qDebug() << "tst_QNetworkSession::closeSession() session state is: " << session->state(); + return result; +} + +void tst_QNetworkSession::sessionAutoClose_data() +{ + QTest::addColumn("configuration"); + + bool testData = false; + foreach (const QNetworkConfiguration &config, + manager.allConfigurations(QNetworkConfiguration::Discovered)) { + QNetworkSession session(config); + if (!session.sessionProperty(QLatin1String("AutoCloseSessionTimeout")).isValid()) + continue; + + testData = true; + + const QString name = config.name().isEmpty() ? QString("") : config.name(); + QTest::newRow(name.toLocal8Bit().constData()) << config; + } + + if (!testData) + QSKIP("No applicable configurations to test", SkipAll); +} + +void tst_QNetworkSession::sessionAutoClose() +{ + QFETCH(QNetworkConfiguration, configuration); + + QNetworkSession session(configuration); + + QVERIFY(session.configuration() == configuration); + + QVariant autoCloseSession = session.sessionProperty(QLatin1String("AutoCloseSessionTimeout")); + + QVERIFY(autoCloseSession.isValid()); + + // property defaults to false + QCOMPARE(autoCloseSession.toInt(), -1); + + QSignalSpy closeSpy(&session, SIGNAL(closed())); + + session.open(); + session.waitForOpened(); + + if (!session.isOpen()) + QSKIP("Session not open", SkipSingle); + + // set session to auto close at next polling interval. + session.setSessionProperty(QLatin1String("AutoCloseSessionTimeout"), 0); + + QTRY_VERIFY(!closeSpy.isEmpty()); + + QCOMPARE(session.state(), QNetworkSession::Connected); + + QVERIFY(!session.isOpen()); + + QVERIFY(session.configuration() == configuration); + + autoCloseSession = session.sessionProperty(QLatin1String("AutoCloseSessionTimeout")); + + QVERIFY(autoCloseSession.isValid()); + + QCOMPARE(autoCloseSession.toInt(), -1); +} + +QTEST_MAIN(tst_QNetworkSession) + +#include "tst_qnetworksession.moc" diff --git a/tests/auto/network/kernel/kernel.pro b/tests/auto/network/kernel/kernel.pro new file mode 100644 index 0000000000..d1c901d529 --- /dev/null +++ b/tests/auto/network/kernel/kernel.pro @@ -0,0 +1,14 @@ +TEMPLATE=subdirs +SUBDIRS=\ + qhostinfo \ +# qnetworkproxyfactory \ # Uses a hardcoded proxy configuration + qauthenticator \ + qnetworkproxy \ + qnetworkinterface \ + qnetworkaddressentry \ + qhostaddress \ + +!contains(QT_CONFIG, private_tests): SUBDIRS -= \ + qauthenticator \ + qhostinfo \ + diff --git a/tests/auto/network/kernel/qauthenticator/qauthenticator.pro b/tests/auto/network/kernel/qauthenticator/qauthenticator.pro new file mode 100644 index 0000000000..8ddb13b0df --- /dev/null +++ b/tests/auto/network/kernel/qauthenticator/qauthenticator.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +requires(contains(QT_CONFIG,private_tests)) +QT = core network-private +SOURCES += tst_qauthenticator.cpp +DEFINES += SRCDIR=\\\"$$PWD/\\\" diff --git a/tests/auto/network/kernel/qauthenticator/tst_qauthenticator.cpp b/tests/auto/network/kernel/qauthenticator/tst_qauthenticator.cpp new file mode 100644 index 0000000000..ae78e10440 --- /dev/null +++ b/tests/auto/network/kernel/qauthenticator/tst_qauthenticator.cpp @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the FOO module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include +#include +#include + +#include + +class tst_QAuthenticator : public QObject +{ + Q_OBJECT + +public: + tst_QAuthenticator(); + +private Q_SLOTS: + void basicAuth(); + void basicAuth_data(); + + void ntlmAuth_data(); + void ntlmAuth(); +}; + +tst_QAuthenticator::tst_QAuthenticator() +{ +} + +void tst_QAuthenticator::basicAuth_data() +{ + QTest::addColumn("data"); + QTest::addColumn("realm"); + QTest::addColumn("user"); + QTest::addColumn("password"); + QTest::addColumn("expectedReply"); + + QTest::newRow("just-user") << "" << "" << "foo" << "" << QByteArray("foo:").toBase64(); + QTest::newRow("user-password") << "" << "" << "foo" << "bar" << QByteArray("foo:bar").toBase64(); + QTest::newRow("user-password-realm") << "realm=\"secure area\"" << "secure area" << "foo" << "bar" << QByteArray("foo:bar").toBase64(); +} + +void tst_QAuthenticator::basicAuth() +{ + QFETCH(QString, data); + QFETCH(QString, realm); + QFETCH(QString, user); + QFETCH(QString, password); + QFETCH(QByteArray, expectedReply); + + QAuthenticator auth; + auth.detach(); + QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(auth); + QVERIFY(priv->phase == QAuthenticatorPrivate::Start); + + QList > headers; + headers << qMakePair(QByteArray("WWW-Authenticate"), "Basic " + data.toUtf8()); + priv->parseHttpResponse(headers, /*isProxy = */ false); + + QCOMPARE(auth.realm(), realm); + QCOMPARE(auth.option("realm").toString(), realm); + + auth.setUser(user); + auth.setPassword(password); + + QVERIFY(priv->phase == QAuthenticatorPrivate::Start); + + QCOMPARE(priv->calculateResponse("GET", "/").constData(), ("Basic " + expectedReply).constData()); +} + +void tst_QAuthenticator::ntlmAuth_data() +{ + QTest::addColumn("data"); + QTest::addColumn("realm"); + + QTest::newRow("no-realm") << "TlRMTVNTUAACAAAAHAAcADAAAAAFAoEATFZ3OLRQADIAAAAAAAAAAJYAlgBMAAAAUQBUAC0AVABFAFMAVAAtAEQATwBNAEEASQBOAAIAHABRAFQALQBUAEUAUwBUAC0ARABPAE0AQQBJAE4AAQAcAFEAVAAtAFQARQBTAFQALQBTAEUAUgBWAEUAUgAEABYAcQB0AC0AdABlAHMAdAAtAG4AZQB0AAMANABxAHQALQB0AGUAcwB0AC0AcwBlAHIAdgBlAHIALgBxAHQALQB0AGUAcwB0AC0AbgBlAHQAAAAAAA==" << ""; + QTest::newRow("with-realm") << "TlRMTVNTUAACAAAADAAMADgAAAAFAoECWCZkccFFAzwAAAAAAAAAAL4AvgBEAAAABQLODgAAAA9NAEcARABOAE8ASwACAAwATQBHAEQATgBPAEsAAQAcAE4ATwBLAC0AQQBNAFMAUwBTAEYARQAtADAAMQAEACAAbQBnAGQAbgBvAGsALgBuAG8AawBpAGEALgBjAG8AbQADAD4AbgBvAGsALQBhAG0AcwBzAHMAZgBlAC0AMAAxAC4AbQBnAGQAbgBvAGsALgBuAG8AawBpAGEALgBjAG8AbQAFACAAbQBnAGQAbgBvAGsALgBuAG8AawBpAGEALgBjAG8AbQAAAAAA" << "NOE"; +} + +void tst_QAuthenticator::ntlmAuth() +{ + QFETCH(QString, data); + QFETCH(QString, realm); + + QAuthenticator auth; + auth.detach(); + QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(auth); + QVERIFY(priv->phase == QAuthenticatorPrivate::Start); + + QList > headers; + + // NTLM phase 1: negotiate + // This phase of NTLM contains no information, other than what we're willing to negotiate + // Current implementation uses flags: + // NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_REQUEST_TARGET + headers << qMakePair("WWW-Authenticate", "NTLM"); + priv->parseHttpResponse(headers, /*isProxy = */ false); + QCOMPARE(priv->calculateResponse("GET", "/").constData(), "NTLM TlRMTVNTUAABAAAABQIAAAAAAAAAAAAAAAAAAAAAAAA="); + + // NTLM phase 2: challenge + headers.clear(); + headers << qMakePair(QByteArray("WWW-Authenticate"), "NTLM " + data.toUtf8()); + priv->parseHttpResponse(headers, /*isProxy = */ false); + + QEXPECT_FAIL("with-realm", "NTLM authentication code doesn't extract the realm", Continue); + QCOMPARE(auth.realm(), realm); + + auth.setUser("unimportant"); + auth.setPassword("unimportant"); + + QVERIFY(!priv->calculateResponse("GET", "/").isEmpty()); +} + +QTEST_MAIN(tst_QAuthenticator); + +#include "tst_qauthenticator.moc" diff --git a/tests/auto/network/kernel/qhostaddress/.gitignore b/tests/auto/network/kernel/qhostaddress/.gitignore new file mode 100644 index 0000000000..dc39ac12d4 --- /dev/null +++ b/tests/auto/network/kernel/qhostaddress/.gitignore @@ -0,0 +1 @@ +tst_qhostaddress diff --git a/tests/auto/network/kernel/qhostaddress/qhostaddress.pro b/tests/auto/network/kernel/qhostaddress/qhostaddress.pro new file mode 100644 index 0000000000..7bcbfb0165 --- /dev/null +++ b/tests/auto/network/kernel/qhostaddress/qhostaddress.pro @@ -0,0 +1,15 @@ +load(qttest_p4) +SOURCES += tst_qhostaddress.cpp + + +QT = core network + +win32: { +wince*: { + LIBS += -lws2 +} else { + LIBS += -lws2_32 +} +} + +symbian: TARGET.CAPABILITY = NetworkServices diff --git a/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp b/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp new file mode 100644 index 0000000000..b20e07be03 --- /dev/null +++ b/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp @@ -0,0 +1,611 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QHostAddress : public QObject +{ + Q_OBJECT + +public: + tst_QHostAddress(); + virtual ~tst_QHostAddress(); + + +public slots: + void init(); + void cleanup(); +private slots: + void constructor_QString_data(); + void constructor_QString(); + void setAddress_QString_data(); + void setAddress_QString(); + void specialAddresses_data(); + void specialAddresses(); + void compare_data(); + void compare(); + void assignment(); + void scopeId(); + void hashKey(); + void streaming_data(); + void streaming(); + void parseSubnet_data(); + void parseSubnet(); + void isInSubnet_data(); + void isInSubnet(); +}; + +QT_BEGIN_NAMESPACE +namespace QTest { + template<> + char *toString(const QHostAddress &addr) + { + if (addr.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol) + return qstrdup(""); + return qstrdup(addr.toString().toLatin1()); + } +} +QT_END_NAMESPACE + +tst_QHostAddress::tst_QHostAddress() +{ +} + +tst_QHostAddress::~tst_QHostAddress() +{ +} + +Q_DECLARE_METATYPE(QHostAddress) + +void tst_QHostAddress::init() +{ + qRegisterMetaType("QHostAddress"); +} + +void tst_QHostAddress::cleanup() +{ + // No cleanup is required. +} + +void tst_QHostAddress::constructor_QString_data() +{ + setAddress_QString_data(); +} + +void tst_QHostAddress::constructor_QString() +{ + QFETCH(QString, address); + QFETCH(bool, ok); + QFETCH(int, protocol); + + QHostAddress hostAddr(address); + + if (address == "0.0.0.0" || address == "::") { + QVERIFY(ok); + } else { + QVERIFY(hostAddr.isNull() != ok); + } + + if (ok) + QTEST(hostAddr.toString(), "resAddr"); + + if ( protocol == 4 ) { + QVERIFY( hostAddr.protocol() == QAbstractSocket::IPv4Protocol || hostAddr.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol ); + QVERIFY( hostAddr.protocol() != QAbstractSocket::IPv6Protocol ); + } else if ( protocol == 6 ) { + QVERIFY( hostAddr.protocol() != QAbstractSocket::IPv4Protocol && hostAddr.protocol() != QAbstractSocket::UnknownNetworkLayerProtocol ); + QVERIFY( hostAddr.protocol() == QAbstractSocket::IPv6Protocol ); + } else { + QVERIFY( hostAddr.isNull() ); + QVERIFY( hostAddr.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol ); + } +} + +void tst_QHostAddress::setAddress_QString_data() +{ + QTest::addColumn("address"); + QTest::addColumn("ok"); + QTest::addColumn("resAddr"); + QTest::addColumn("protocol"); // 4: IPv4, 6: IPv6, other: undefined + + //next we fill it with data + QTest::newRow("ip4_00") << QString("127.0.0.1") << (bool)TRUE << QString("127.0.0.1") << 4; + QTest::newRow("ip4_01") << QString("255.3.2.1") << (bool)TRUE << QString("255.3.2.1") << 4; + QTest::newRow("ip4_03") << QString(" 255.3.2.1") << (bool)TRUE << QString("255.3.2.1") << 4; + QTest::newRow("ip4_04") << QString("255.3.2.1\r ") << (bool)TRUE << QString("255.3.2.1") << 4; + QTest::newRow("ip4_05") << QString("0.0.0.0") << (bool)TRUE << QString("0.0.0.0") << 4; + + // for the format of IPv6 addresses see also RFC 5952 + QTest::newRow("ip6_00") << QString("FEDC:BA98:7654:3210:FEDC:BA98:7654:3210") << (bool)TRUE << QString("FEDC:BA98:7654:3210:FEDC:BA98:7654:3210") << 6; + QTest::newRow("ip6_01") << QString("1080:0000:0000:0000:0008:0800:200C:417A") << (bool)TRUE << QString("1080::8:800:200C:417A") << 6; + QTest::newRow("ip6_02") << QString("1080:0:0:0:8:800:200C:417A") << (bool)TRUE << QString("1080::8:800:200C:417A") << 6; + QTest::newRow("ip6_03") << QString("1080::8:800:200C:417A") << (bool)TRUE << QString("1080::8:800:200C:417A") << 6; + QTest::newRow("ip6_04") << QString("FF01::43") << (bool)TRUE << QString("FF01::43") << 6; + QTest::newRow("ip6_05") << QString("::1") << (bool)TRUE << QString("::1") << 6; + QTest::newRow("ip6_06") << QString("1::") << (bool)TRUE << QString("1::") << 6; + QTest::newRow("ip6_07") << QString("::") << (bool)TRUE << QString("::") << 6; + QTest::newRow("ip6_08") << QString("0:0:0:0:0:0:13.1.68.3") << (bool)TRUE << QString("::D01:4403") << 6; + QTest::newRow("ip6_09") << QString("::13.1.68.3") << (bool)TRUE << QString("::D01:4403") << 6; + QTest::newRow("ip6_10") << QString("0:0:0:0:0:FFFF:129.144.52.38") << (bool)TRUE << QString("::FFFF:8190:3426") << 6; + QTest::newRow("ip6_11") << QString("::FFFF:129.144.52.38") << (bool)TRUE << QString("::FFFF:8190:3426") << 6; + QTest::newRow("ip6_12") << QString("1::FFFF:129.144.52.38") << (bool)TRUE << QString("1::FFFF:8190:3426") << 6; + QTest::newRow("ip6_13") << QString("A:B::D:E") << (bool)TRUE << QString("A:B::D:E") << 6; + QTest::newRow("ip6_14") << QString("1080:0:1:0:8:800:200C:417A") << (bool)TRUE << QString("1080:0:1:0:8:800:200C:417A") << 6; + QTest::newRow("ip6_15") << QString("1080:0:1:0:8:800:200C:0") << (bool)TRUE << QString("1080:0:1:0:8:800:200C:0") << 6; + QTest::newRow("ip6_16") << QString("1080:0:1:0:8:800:0:0") << (bool)TRUE << QString("1080:0:1:0:8:800::") << 6; + QTest::newRow("ip6_17") << QString("1080:0:0:0:8:800:0:0") << (bool)TRUE << QString("1080::8:800:0:0") << 6; + QTest::newRow("ip6_18") << QString("0:1:1:1:8:800:0:0") << (bool)TRUE << QString("0:1:1:1:8:800::") << 6; + QTest::newRow("ip6_19") << QString("0:1:1:1:8:800:0:1") << (bool)TRUE << QString("0:1:1:1:8:800:0:1") << 6; + + QTest::newRow("error_00") << QString("foobarcom") << (bool)FALSE << QString() << 0; + QTest::newRow("error_01") << QString("foo.bar.com") << (bool)FALSE << QString() << 0; + QTest::newRow("error_02") << QString("") << (bool)FALSE << QString() << 0; + QTest::newRow("error_03") << QString() << (bool)FALSE << QString() << 0; + QTest::newRow("error_04") << QString(" \t\r") << (bool)FALSE << QString() << 0; + + QTest::newRow("error_ip4_00") << QString("256.9.9.9") << (bool)FALSE << QString() << 0; + QTest::newRow("error_ip4_01") << QString("-1.9.9.9") << (bool)FALSE << QString() << 0; + QTest::newRow("error_ip4_02") << QString("123.0.0") << (bool)FALSE << QString() << 0; + QTest::newRow("error_ip4_03") << QString("123.0.0.0.0") << (bool)FALSE << QString() << 0; + QTest::newRow("error_ip4_04") << QString("255.2 3.2.1") << (bool)FALSE << QString() << 0; + + QTest::newRow("error_ip6_00") << QString(":") << (bool)FALSE << QString() << 0; + QTest::newRow("error_ip6_01") << QString(":::") << (bool)FALSE << QString() << 0; + QTest::newRow("error_ip6_02") << QString("::AAAA:") << (bool)FALSE << QString() << 0; + QTest::newRow("error_ip6_03") << QString(":AAAA::") << (bool)FALSE << QString() << 0; + QTest::newRow("error_ip6_04") << QString("FFFF:::129.144.52.38") << (bool)FALSE << QString() << 0; + QTest::newRow("error_ip6_05") << QString("FEDC:BA98:7654:3210:FEDC:BA98:7654:3210:1234") << (bool)FALSE << QString() << 0; + QTest::newRow("error_ip6_06") << QString("129.144.52.38::") << (bool)FALSE << QString() << 0; + QTest::newRow("error_ip6_07") << QString("::129.144.52.38:129.144.52.38") << (bool)FALSE << QString() << 0; + QTest::newRow("error_ip6_08") << QString(":::129.144.52.38") << (bool)FALSE << QString() << 0; + QTest::newRow("error_ip6_09") << QString("1FEDC:BA98:7654:3210:FEDC:BA98:7654:3210") << (bool)FALSE << QString() << 0; + QTest::newRow("error_ip6_10") << QString("::FFFFFFFF") << (bool)FALSE << QString() << 0; + QTest::newRow("error_ip6_11") << QString("::EFGH") << (bool)FALSE << QString() << 0; + QTest::newRow("error_ip6_12") << QString("ABCD:ABCD:ABCD") << (bool)FALSE << QString() << 0; + QTest::newRow("error_ip6_13") << QString("::ABCD:ABCD::") << (bool)FALSE << QString() << 0; + QTest::newRow("error_ip6_14") << QString("1::2::3") << (bool)FALSE << QString() << 0; + QTest::newRow("error_ip6_15") << QString("1:2:::") << (bool)FALSE << QString() << 0; + QTest::newRow("error_ip6_16") << QString(":::1:2") << (bool)FALSE << QString() << 0; + QTest::newRow("error_ip6_17") << QString("1:::2") << (bool)FALSE << QString() << 0; + QTest::newRow("error_ip6_18") << QString("FEDC::7654:3210:FEDC:BA98::3210") << (bool)FALSE << QString() << 0; + QTest::newRow("error_ip6_19") << QString("ABCD:ABCD:ABCD:1.2.3.4") << (bool)FALSE << QString() << 0; + QTest::newRow("error_ip6_20") << QString("ABCD::ABCD::ABCD:1.2.3.4") << (bool)FALSE << QString() << 0; + +} + +void tst_QHostAddress::setAddress_QString() +{ + QFETCH(QString, address); + QFETCH(bool, ok); + QFETCH(int, protocol); + + QHostAddress hostAddr; + QVERIFY(hostAddr.setAddress(address) == ok); + + if (ok) + QTEST(hostAddr.toString(), "resAddr"); + + if ( protocol == 4 ) { + QVERIFY( hostAddr.protocol() == QAbstractSocket::IPv4Protocol || hostAddr.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol ); + QVERIFY( hostAddr.protocol() != QAbstractSocket::IPv6Protocol ); + } else if ( protocol == 6 ) { + QVERIFY( hostAddr.protocol() != QAbstractSocket::IPv4Protocol && hostAddr.protocol() != QAbstractSocket::UnknownNetworkLayerProtocol ); + QVERIFY( hostAddr.protocol() == QAbstractSocket::IPv6Protocol ); + } else { + QVERIFY( hostAddr.isNull() ); + QVERIFY( hostAddr.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol ); + } +} + +void tst_QHostAddress::specialAddresses_data() +{ + QTest::addColumn("text"); + QTest::addColumn("address"); + QTest::addColumn("result"); + + QTest::newRow("localhost_1") << QString("127.0.0.1") << (int)QHostAddress::LocalHost << true; + QTest::newRow("localhost_2") << QString("127.0.0.2") << (int)QHostAddress::LocalHost << false; + QTest::newRow("localhost_3") << QString("127.0.0.2") << (int)QHostAddress::LocalHostIPv6 << false; + + QTest::newRow("localhost_ipv6_4") << QString("::1") << (int)QHostAddress::LocalHostIPv6 << true; + QTest::newRow("localhost_ipv6_5") << QString("::2") << (int)QHostAddress::LocalHostIPv6 << false; + QTest::newRow("localhost_ipv6_6") << QString("::1") << (int)QHostAddress::LocalHost << false; + + QTest::newRow("null_1") << QString("") << (int)QHostAddress::Null << true; + QTest::newRow("null_2") << QString("bjarne") << (int)QHostAddress::Null << true; + + QTest::newRow("compare_from_null") << QString("") << (int)QHostAddress::Broadcast << false; + + QTest::newRow("broadcast_1") << QString("255.255.255.255") << (int)QHostAddress::Any << false; + QTest::newRow("broadcast_2") << QString("255.255.255.255") << (int)QHostAddress::Broadcast << true; + + QTest::newRow("any_ipv6") << QString("::") << (int)QHostAddress::AnyIPv6 << true; + QTest::newRow("any_ipv4") << QString("0.0.0.0") << (int)QHostAddress::AnyIPv4 << true; + + QTest::newRow("dual_not_ipv6") << QString("::") << (int)QHostAddress::Any << false; + QTest::newRow("dual_not_ipv4") << QString("0.0.0.0") << (int)QHostAddress::Any << false; +} + + +void tst_QHostAddress::specialAddresses() +{ + QFETCH(QString, text); + QFETCH(int, address); + QFETCH(bool, result); + QVERIFY((QHostAddress(text) == (QHostAddress::SpecialAddress)address) == result); + + QHostAddress setter; + setter.setAddress(text); + if (result) { + QVERIFY(setter == (QHostAddress::SpecialAddress) address); + } else { + QVERIFY(!((QHostAddress::SpecialAddress) address == setter)); + } +} + + +void tst_QHostAddress::compare_data() +{ + QTest::addColumn("first"); + QTest::addColumn("second"); + QTest::addColumn("result"); + + QTest::newRow("1") << QHostAddress() << QHostAddress() << true; + QTest::newRow("2") << QHostAddress(QHostAddress::Any) << QHostAddress(QHostAddress::Any) << true; + QTest::newRow("3") << QHostAddress(QHostAddress::AnyIPv6) << QHostAddress(QHostAddress::AnyIPv6) << true; + QTest::newRow("4") << QHostAddress(QHostAddress::Broadcast) << QHostAddress(QHostAddress::Broadcast) << true; + QTest::newRow("5") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::Broadcast) << false; + QTest::newRow("6") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::LocalHostIPv6) << false; + QTest::newRow("7") << QHostAddress() << QHostAddress(QHostAddress::LocalHostIPv6) << false; +} + +void tst_QHostAddress::compare() +{ + QFETCH(QHostAddress, first); + QFETCH(QHostAddress, second); + QFETCH(bool, result); + + QCOMPARE(first == second, result); +} + +void tst_QHostAddress::assignment() +{ + QHostAddress address; + address = "127.0.0.1"; + QCOMPARE(address, QHostAddress("127.0.0.1")); + + address = "::1"; + QCOMPARE(address, QHostAddress("::1")); + + QHostAddress addr("4.2.2.1"); + sockaddr_in sockAddr; + sockAddr.sin_family = AF_INET; + sockAddr.sin_addr.s_addr = htonl(addr.toIPv4Address()); + address.setAddress((sockaddr *)&sockAddr); + QCOMPARE(address, addr); +} + +void tst_QHostAddress::scopeId() +{ + QHostAddress address("fe80::2e0:4cff:fefb:662a%eth0"); + QCOMPARE(address.scopeId(), QString("eth0")); + QCOMPARE(address.toString().toLower(), QString("fe80::2e0:4cff:fefb:662a%eth0")); + + QHostAddress address2("fe80::2e0:4cff:fefb:662a"); + QCOMPARE(address2.scopeId(), QString()); + address2.setScopeId(QString("en0")); + QCOMPARE(address2.toString().toLower(), QString("fe80::2e0:4cff:fefb:662a%en0")); + + address2 = address; + QCOMPARE(address2.scopeId(), QString("eth0")); + QCOMPARE(address2.toString().toLower(), QString("fe80::2e0:4cff:fefb:662a%eth0")); +} + +void tst_QHostAddress::hashKey() +{ + QHash hostHash; + hostHash.insert(QHostAddress(), "ole"); +} + +void tst_QHostAddress::streaming_data() +{ + QTest::addColumn("address"); + QTest::newRow("1") << QHostAddress(); + QTest::newRow("2") << QHostAddress(0xDEADBEEF); + QTest::newRow("3") << QHostAddress("127.128.129.130"); + QTest::newRow("4") << QHostAddress("1080:0000:0000:0000:0008:0800:200C:417A"); + QTest::newRow("5") << QHostAddress("fe80::2e0:4cff:fefb:662a%eth0"); + QTest::newRow("6") << QHostAddress(QHostAddress::Null); + QTest::newRow("7") << QHostAddress(QHostAddress::LocalHost); + QTest::newRow("8") << QHostAddress(QHostAddress::LocalHostIPv6); + QTest::newRow("9") << QHostAddress(QHostAddress::Broadcast); + QTest::newRow("10") << QHostAddress(QHostAddress::Any); + QTest::newRow("11") << QHostAddress(QHostAddress::AnyIPv4); + QTest::newRow("12") << QHostAddress(QHostAddress::AnyIPv6); + QTest::newRow("13") << QHostAddress("foo.bar.com"); +} + +void tst_QHostAddress::streaming() +{ + QFETCH(QHostAddress, address); + QByteArray ba; + QDataStream ds1(&ba, QIODevice::WriteOnly); + ds1 << address; + QVERIFY(ds1.status() == QDataStream::Ok); + QDataStream ds2(&ba, QIODevice::ReadOnly); + QHostAddress address2; + ds2 >> address2; + QVERIFY(ds2.status() == QDataStream::Ok); + QCOMPARE(address, address2); +} + +void tst_QHostAddress::parseSubnet_data() +{ + QTest::addColumn("subnet"); + QTest::addColumn("prefix"); + QTest::addColumn("prefixLength"); + + // invalid/error values + QTest::newRow("empty") << QString() << QHostAddress() << -1; + QTest::newRow("invalid_01") << "foobar" << QHostAddress() << -1; + QTest::newRow("invalid_02") << " " << QHostAddress() << -1; + QTest::newRow("invalid_03") << "1.2.3.a" << QHostAddress() << -1; + QTest::newRow("invalid_04") << "1.2.3.4.5" << QHostAddress() << -1; + QTest::newRow("invalid_05") << "1.2.3.4:80" << QHostAddress() << -1; + QTest::newRow("invalid_06") << "1.2.3.4/33" << QHostAddress() << -1; + QTest::newRow("invalid_07") << "1.2.3.4/-1" << QHostAddress() << -1; + QTest::newRow("invalid_08") << "1.2.3.4/256.0.0.0" << QHostAddress() << -1; + QTest::newRow("invalid_09") << "1.2.3.4/255.253.0.0" << QHostAddress() << -1; + QTest::newRow("invalid_10") << "1.2.3.4/255.0.0.255" << QHostAddress() << -1; + QTest::newRow("invalid_11") << "1.2.3.4." << QHostAddress() << -1; + QTest::newRow("invalid_20") << "ffff::/-1" << QHostAddress() << -1; + QTest::newRow("invalid_21") << "ffff::/129" << QHostAddress() << -1; + QTest::newRow("invalid_22") << "ffff::/255.255.0.0" << QHostAddress() << -1; + QTest::newRow("invalid_23") << "ffff::/ff00::" << QHostAddress() << -1; + + // correct IPv4 with netmask + QTest::newRow("netmask_0") << "0.0.0.0/0.0.0.0" << QHostAddress(QHostAddress::AnyIPv4) << 0; + QTest::newRow("netmask_1") << "0.0.0.0/255.128.0.0" << QHostAddress(QHostAddress::AnyIPv4) << 9; + QTest::newRow("netmask_2") << "0.0.0.0/255.192.0.0" << QHostAddress(QHostAddress::AnyIPv4) << 10; + QTest::newRow("netmask_3") << "0.0.0.0/255.224.0.0" << QHostAddress(QHostAddress::AnyIPv4) << 11; + QTest::newRow("netmask_4") << "0.0.0.0/255.240.0.0" << QHostAddress(QHostAddress::AnyIPv4) << 12; + QTest::newRow("netmask_5") << "0.0.0.0/255.248.0.0" << QHostAddress(QHostAddress::AnyIPv4) << 13; + QTest::newRow("netmask_6") << "0.0.0.0/255.252.0.0" << QHostAddress(QHostAddress::AnyIPv4) << 14; + QTest::newRow("netmask_7") << "0.0.0.0/255.254.0.0" << QHostAddress(QHostAddress::AnyIPv4) << 15; + QTest::newRow("netmask_8") << "0.0.0.0/255.255.0.0" << QHostAddress(QHostAddress::AnyIPv4) << 16; + QTest::newRow("netmask_16") << "0.0.0.0/255.255.0.0" << QHostAddress(QHostAddress::AnyIPv4) << 16; + QTest::newRow("netmask_24") << "0.0.0.0/255.255.255.0" << QHostAddress(QHostAddress::AnyIPv4) << 24; + QTest::newRow("netmask_31") << "0.0.0.0/255.255.255.254" << QHostAddress(QHostAddress::AnyIPv4) << 31; + QTest::newRow("netmask_32") << "0.0.0.0/255.255.255.255" << QHostAddress(QHostAddress::AnyIPv4) << 32; + + // correct IPv4 with prefix + QTest::newRow("prefix_0") << "0.0.0.0/0" << QHostAddress(QHostAddress::AnyIPv4) << 0; + QTest::newRow("prefix_1") << "0.0.0.0/1" << QHostAddress(QHostAddress::AnyIPv4) << 1; + QTest::newRow("prefix_9") << "0.0.0.0/9" << QHostAddress(QHostAddress::AnyIPv4) << 9; + QTest::newRow("prefix_31") << "0.0.0.0/31" << QHostAddress(QHostAddress::AnyIPv4) << 31; + QTest::newRow("prefix_32") << "0.0.0.0/32" << QHostAddress(QHostAddress::AnyIPv4) << 32; + + // correct IPv4 without prefix or netmask + QTest::newRow("classA") << "10" << QHostAddress("10.0.0.0") << 8; + QTest::newRow("classA+dot") << "10." << QHostAddress("10.0.0.0") << 8; + QTest::newRow("classB") << "172.16" << QHostAddress("172.16.0.0") << 16; + QTest::newRow("classB+dot") << "172.16." << QHostAddress("172.16.0.0") << 16; + QTest::newRow("classC") << "192.168.0" << QHostAddress("192.168.0.0") << 24; + QTest::newRow("classC+dot") << "192.168.0" << QHostAddress("192.168.0.0") << 24; + QTest::newRow("full-ipv4") << "192.168.0.1" << QHostAddress("192.168.0.1") << 32; + + // correct IPv6 with prefix + QTest::newRow("ipv6_01") << "::/0" << QHostAddress(QHostAddress::AnyIPv6) << 0; + QTest::newRow("ipv6_03") << "::/3" << QHostAddress(QHostAddress::AnyIPv6) << 3; + QTest::newRow("ipv6_16") << "::/16" << QHostAddress(QHostAddress::AnyIPv6) << 16; + QTest::newRow("ipv6_48") << "::/48" << QHostAddress(QHostAddress::AnyIPv6) << 48; + QTest::newRow("ipv6_127") << "::/127" << QHostAddress(QHostAddress::AnyIPv6) << 127; + QTest::newRow("ipv6_128") << "::/128" << QHostAddress(QHostAddress::AnyIPv6) << 128; + + // tail bit clearing: + QTest::newRow("clear_01") << "255.255.255.255/31" << QHostAddress("255.255.255.254") << 31; + QTest::newRow("clear_08") << "255.255.255.255/24" << QHostAddress("255.255.255.0") << 24; + QTest::newRow("clear_09") << "255.255.255.255/23" << QHostAddress("255.255.254.0") << 23; + QTest::newRow("clear_10") << "255.255.255.255/22" << QHostAddress("255.255.252.0") << 22; + QTest::newRow("clear_11") << "255.255.255.255/21" << QHostAddress("255.255.248.0") << 21; + QTest::newRow("clear_12") << "255.255.255.255/20" << QHostAddress("255.255.240.0") << 20; + QTest::newRow("clear_13") << "255.255.255.255/19" << QHostAddress("255.255.224.0") << 19; + QTest::newRow("clear_14") << "255.255.255.255/18" << QHostAddress("255.255.192.0") << 18; + QTest::newRow("clear_15") << "255.255.255.255/17" << QHostAddress("255.255.128.0") << 17; + QTest::newRow("clear_16") << "255.255.255.255/16" << QHostAddress("255.255.0.0") << 16; + QTest::newRow("clear_24") << "255.255.255.255/8" << QHostAddress("255.0.0.0") << 8; + QTest::newRow("clear_31") << "255.255.255.255/1" << QHostAddress("128.0.0.0") << 1; + QTest::newRow("clear_32") << "255.255.255.255/0" << QHostAddress("0.0.0.0") << 0; + + // same for IPv6: + QTest::newRow("ipv6_clear_01") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/127" + << QHostAddress("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe") + << 127; + QTest::newRow("ipv6_clear_07") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/121" + << QHostAddress("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff80") + << 121; + QTest::newRow("ipv6_clear_08") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/120" + << QHostAddress("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00") + << 120; + QTest::newRow("ipv6_clear_16") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/112" + << QHostAddress("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0") + << 112; + QTest::newRow("ipv6_clear_80") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/48" + << QHostAddress("ffff:ffff:ffff::") + << 48; + QTest::newRow("ipv6_clear_81") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/47" + << QHostAddress("ffff:ffff:fffe::") + << 47; + QTest::newRow("ipv6_clear_82") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/46" + << QHostAddress("ffff:ffff:fffc::") + << 46; + QTest::newRow("ipv6_clear_83") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/45" + << QHostAddress("ffff:ffff:fff8::") + << 45; + QTest::newRow("ipv6_clear_84") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/44" + << QHostAddress("ffff:ffff:fff0::") + << 44; + QTest::newRow("ipv6_clear_85") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/43" + << QHostAddress("ffff:ffff:ffe0::") + << 43; + QTest::newRow("ipv6_clear_86") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/42" + << QHostAddress("ffff:ffff:ffc0::") + << 42; + QTest::newRow("ipv6_clear_87") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/41" + << QHostAddress("ffff:ffff:ff80::") + << 41; + QTest::newRow("ipv6_clear_88") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/40" + << QHostAddress("ffff:ffff:ff00::") + << 40; + QTest::newRow("ipv6_clear_125") << "3fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/3" + << QHostAddress("2000::") + << 3; + QTest::newRow("ipv6_clear_127") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/1" + << QHostAddress("8000::") + << 1; + QTest::newRow("ipv6_clear_128") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/0" + << QHostAddress(QHostAddress::AnyIPv6) + << 0; +} + +void tst_QHostAddress::parseSubnet() +{ + QFETCH(QString, subnet); + QFETCH(QHostAddress, prefix); + QFETCH(int, prefixLength); + + QPair result = QHostAddress::parseSubnet(subnet); + QCOMPARE(result.first, prefix); + QCOMPARE(result.second, prefixLength); +} + +void tst_QHostAddress::isInSubnet_data() +{ + QTest::addColumn("address"); + QTest::addColumn("prefix"); + QTest::addColumn("prefixLength"); + QTest::addColumn("result"); + + // invalid QHostAddresses are never in any subnets + QTest::newRow("invalid_01") << QHostAddress() << QHostAddress() << 32 << false; + QTest::newRow("invalid_02") << QHostAddress() << QHostAddress(QHostAddress::AnyIPv4) << 32 << false; + QTest::newRow("invalid_03") << QHostAddress() << QHostAddress(QHostAddress::AnyIPv4) << 8 << false; + QTest::newRow("invalid_04") << QHostAddress() << QHostAddress(QHostAddress::AnyIPv4) << 0 << false; + QTest::newRow("invalid_05") << QHostAddress() << QHostAddress("255.255.255.0") << 24 << false; + QTest::newRow("invalid_06") << QHostAddress() << QHostAddress(QHostAddress::AnyIPv6) << 0 << false; + QTest::newRow("invalid_07") << QHostAddress() << QHostAddress(QHostAddress::AnyIPv6) << 32 << false; + QTest::newRow("invalid_08") << QHostAddress() << QHostAddress(QHostAddress::AnyIPv6) << 128<< false; + + // and no host address can be in a subnet whose prefix is invalid + QTest::newRow("invalid_20") << QHostAddress(QHostAddress::AnyIPv4) << QHostAddress() << 16 << false; + QTest::newRow("invalid_21") << QHostAddress(QHostAddress::AnyIPv6) << QHostAddress() << 16 << false; + QTest::newRow("invalid_22") << QHostAddress(QHostAddress::LocalHost) << QHostAddress() << 16 << false; + QTest::newRow("invalid_23") << QHostAddress(QHostAddress::LocalHostIPv6) << QHostAddress() << 16 << false; + + // negative netmasks don't make sense: + QTest::newRow("invalid_30") << QHostAddress(QHostAddress::AnyIPv4) << QHostAddress(QHostAddress::Any) << -1 << false; + QTest::newRow("invalid_31") << QHostAddress(QHostAddress::AnyIPv6) << QHostAddress(QHostAddress::AnyIPv6) << -1 << false; + + // we don't support IPv4 belonging in an IPv6 netmask and vice-versa + QTest::newRow("v4-in-v6") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::AnyIPv6) << 0 << false; + QTest::newRow("v6-in-v4") << QHostAddress(QHostAddress::LocalHostIPv6) << QHostAddress(QHostAddress::Any) << 0 << false; + QTest::newRow("v4-in-v6mapped") << QHostAddress(QHostAddress::LocalHost) << QHostAddress("ffff:ffff:ffff:ffff:ffff:ffff:255.0.0.0") << 113 << false; + QTest::newRow("v4-in-v6mapped2") << QHostAddress(QHostAddress::LocalHost) << QHostAddress("::ffff:255.0.0.0") << 113 << false; + + // IPv4 correct ones + QTest::newRow("netmask_0") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::AnyIPv4) << 0 << true; + QTest::newRow("netmask_0bis") << QHostAddress(QHostAddress::LocalHost) << QHostAddress("255.255.0.0") << 0 << true; + QTest::newRow("netmask_0ter") << QHostAddress(QHostAddress::LocalHost) << QHostAddress("1.2.3.4") << 0 << true; + QTest::newRow("netmask_1") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::AnyIPv4) << 1 << true; + QTest::newRow("~netmask_1") << QHostAddress(QHostAddress::LocalHost) << QHostAddress("128.0.0.0") << 1 << false; + QTest::newRow("netmask_1bis") << QHostAddress("224.0.0.1") << QHostAddress("128.0.0.0") << 1 << true; + QTest::newRow("~netmask_1bis") << QHostAddress("224.0.0.1") << QHostAddress("0.0.0.0") << 1 << false; + QTest::newRow("netmask_8") << QHostAddress(QHostAddress::LocalHost) << QHostAddress("127.0.0.0") << 8 << true; + QTest::newRow("~netmask_8") << QHostAddress(QHostAddress::LocalHost) << QHostAddress("126.0.0.0") << 8 << false; + QTest::newRow("netmask_15") << QHostAddress("10.0.1.255") << QHostAddress("10.0.0.0") << 15 << true; + QTest::newRow("netmask_16") << QHostAddress("172.16.0.1") << QHostAddress("172.16.0.0") << 16 << true; + + // the address is always in the subnet containing its address, regardless of length: + QTest::newRow("same_01") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::LocalHost) << 1 << true; + QTest::newRow("same_07") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::LocalHost) << 7 << true; + QTest::newRow("same_8") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::LocalHost) << 8 << true; + QTest::newRow("same_24") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::LocalHost) << 23 << true; + QTest::newRow("same_31") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::LocalHost) << 31 << true; + QTest::newRow("same_32") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::LocalHost) << 32 << true; + + // IPv6 correct ones: + QTest::newRow("ipv6_netmask_0") << QHostAddress(QHostAddress::LocalHostIPv6) << QHostAddress(QHostAddress::AnyIPv6) << 0 << true; + QTest::newRow("ipv6_netmask_0bis") << QHostAddress(QHostAddress::LocalHostIPv6) << QHostAddress(QHostAddress::LocalHostIPv6) << 0 << true; + QTest::newRow("ipv6_netmask_0ter") << QHostAddress(QHostAddress::LocalHostIPv6) << QHostAddress("ffff::") << 0 << true; + QTest::newRow("ipv6_netmask_1") << QHostAddress(QHostAddress::LocalHostIPv6) << QHostAddress(QHostAddress::AnyIPv6) << 1 << true; + QTest::newRow("ipv6_netmask_1bis") << QHostAddress("fec0::1") << QHostAddress("8000::") << 1 << true; + QTest::newRow("~ipv6_netmask_1") << QHostAddress(QHostAddress::LocalHostIPv6) << QHostAddress("8000::") << 1 << false; + QTest::newRow("~ipv6_netmask_1bis") << QHostAddress("fec0::1") << QHostAddress("::") << 1 << false; + QTest::newRow("ipv6_netmask_47") << QHostAddress("2:3:5::1") << QHostAddress("2:3:4::") << 47 << true; + QTest::newRow("ipv6_netmask_48") << QHostAddress("2:3:4::1") << QHostAddress("2:3:4::") << 48 << true; + QTest::newRow("~ipv6_netmask_48") << QHostAddress("2:3:5::1") << QHostAddress("2:3:4::") << 48 << false; + QTest::newRow("ipv6_netmask_127") << QHostAddress("2:3:4:5::1") << QHostAddress("2:3:4:5::") << 127 << true; + QTest::newRow("ipv6_netmask_128") << QHostAddress("2:3:4:5::1") << QHostAddress("2:3:4:5::1") << 128 << true; + QTest::newRow("~ipv6_netmask_128") << QHostAddress("2:3:4:5::1") << QHostAddress("2:3:4:5::0") << 128 << false; +} + +void tst_QHostAddress::isInSubnet() +{ + QFETCH(QHostAddress, address); + QFETCH(QHostAddress, prefix); + QFETCH(int, prefixLength); + + QTEST(address.isInSubnet(prefix, prefixLength), "result"); +} + +QTEST_MAIN(tst_QHostAddress) +#include "tst_qhostaddress.moc" diff --git a/tests/auto/network/kernel/qhostinfo/.gitignore b/tests/auto/network/kernel/qhostinfo/.gitignore new file mode 100644 index 0000000000..432954e8d8 --- /dev/null +++ b/tests/auto/network/kernel/qhostinfo/.gitignore @@ -0,0 +1 @@ +tst_qhostinfo diff --git a/tests/auto/network/kernel/qhostinfo/qhostinfo.pro b/tests/auto/network/kernel/qhostinfo/qhostinfo.pro new file mode 100644 index 0000000000..77ce1867d1 --- /dev/null +++ b/tests/auto/network/kernel/qhostinfo/qhostinfo.pro @@ -0,0 +1,16 @@ +load(qttest_p4) + +SOURCES += tst_qhostinfo.cpp + +QT = core-private network-private + +wince*: { + LIBS += ws2.lib +} else { + win32:LIBS += -lws2_32 +} + +symbian: TARGET.CAPABILITY = NetworkServices +symbian: { + INCLUDEPATH *= $$MW_LAYER_SYSTEMINCLUDE +} diff --git a/tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp b/tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp new file mode 100644 index 0000000000..58b51bcb69 --- /dev/null +++ b/tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp @@ -0,0 +1,665 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +// When using WinSock2 on Windows, it's the first thing that can be included +// (except qglobal.h), or else you'll get tons of compile errors +#include + +// MinGW doesn't provide getaddrinfo(), so we test for Q_OS_WIN +// and Q_CC_GNU, which indirectly tells us whether we're using MinGW. +#if defined(Q_OS_WIN) && defined(Q_CC_GNU) +# define QT_NO_GETADDRINFO +#endif + +#if defined(Q_OS_WIN) && !defined(QT_NO_GETADDRINFO) +# include +# include +#endif + +#include +#include +#include +#include +#include +#include + +#ifndef QT_NO_BEARERMANAGEMENT +#include +#include +#include +#endif + +#include +#include +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) +#include +#else +#include +#include +#endif + +#include +#include "private/qhostinfo_p.h" + +#if !defined(QT_NO_GETADDRINFO) +# if !defined(Q_OS_WINCE) +# include +# else +# include +# endif +# if defined(Q_OS_UNIX) +# include +# endif +# if !defined(Q_OS_WIN) +# include +# endif +#endif + +#include "../../../network-settings.h" +#include "../../../../shared/util.h" + +//TESTED_CLASS= +//TESTED_FILES= + +const char * const lupinellaIp = "10.3.4.6"; + + +class tst_QHostInfo : public QObject +{ + Q_OBJECT + +public: + tst_QHostInfo(); + virtual ~tst_QHostInfo(); + + +public slots: + void init(); + void cleanup(); + void initTestCase(); + +private slots: + void getSetCheck(); + void staticInformation(); + void lookupIPv4_data(); + void lookupIPv4(); + void lookupIPv6_data(); + void lookupIPv6(); + void reverseLookup_data(); + void reverseLookup(); + + void blockingLookup_data(); + void blockingLookup(); + + void raceCondition(); + void threadSafety(); + void threadSafetyAsynchronousAPI(); + + void multipleSameLookups(); + void multipleDifferentLookups_data(); + void multipleDifferentLookups(); + + void cache(); + + void abortHostLookup(); + void abortHostLookupInDifferentThread(); +protected slots: + void resultsReady(const QHostInfo &); + +private: + bool ipv6LookupsAvailable; + bool ipv6Available; + bool lookupDone; + int lookupsDoneCounter; + QHostInfo lookupResults; +#ifndef QT_NO_BEARERMANAGEMENT + QNetworkConfigurationManager *netConfMan; + QNetworkConfiguration networkConfiguration; + QScopedPointer networkSession; +#endif +}; + +// Testing get/set functions +void tst_QHostInfo::getSetCheck() +{ + QHostInfo obj1; + // HostInfoError QHostInfo::error() + // void QHostInfo::setError(HostInfoError) + obj1.setError(QHostInfo::HostInfoError(0)); + QCOMPARE(QHostInfo::HostInfoError(0), obj1.error()); + obj1.setError(QHostInfo::HostInfoError(1)); + QCOMPARE(QHostInfo::HostInfoError(1), obj1.error()); + + // int QHostInfo::lookupId() + // void QHostInfo::setLookupId(int) + obj1.setLookupId(0); + QCOMPARE(0, obj1.lookupId()); + obj1.setLookupId(INT_MIN); + QCOMPARE(INT_MIN, obj1.lookupId()); + obj1.setLookupId(INT_MAX); + QCOMPARE(INT_MAX, obj1.lookupId()); +} + +void tst_QHostInfo::staticInformation() +{ + qDebug() << "Hostname:" << QHostInfo::localHostName(); + qDebug() << "Domain name:" << QHostInfo::localDomainName(); +} + +tst_QHostInfo::tst_QHostInfo() +{ + Q_SET_DEFAULT_IAP +} + +tst_QHostInfo::~tst_QHostInfo() +{ +} + +void tst_QHostInfo::initTestCase() +{ +#ifndef QT_NO_BEARERMANAGEMENT + //start the default network + netConfMan = new QNetworkConfigurationManager(this); + networkConfiguration = netConfMan->defaultConfiguration(); + networkSession.reset(new QNetworkSession(networkConfiguration)); + if (!networkSession->isOpen()) { + networkSession->open(); + QVERIFY(networkSession->waitForOpened(30000)); + } +#endif + +#ifdef Q_OS_SYMBIAN + ipv6Available = true; + ipv6LookupsAvailable = true; +#else + ipv6Available = false; + ipv6LookupsAvailable = false; + + QTcpServer server; + if (server.listen(QHostAddress("::1"))) { + // We have IPv6 support + ipv6Available = true; + } + +#if !defined(QT_NO_GETADDRINFO) + // check if the system getaddrinfo can do IPv6 lookups + struct addrinfo hint, *result = 0; + memset(&hint, 0, sizeof hint); + hint.ai_family = AF_UNSPEC; +# ifdef AI_ADDRCONFIG + hint.ai_flags = AI_ADDRCONFIG; +# endif + + int res = getaddrinfo("::1", "80", &hint, &result); + if (res == 0) { + // this test worked + freeaddrinfo(result); + res = getaddrinfo("ipv6-test.dev.troll.no", "80", &hint, &result); + if (res == 0 && result != 0 && result->ai_family != AF_INET) { + freeaddrinfo(result); + ipv6LookupsAvailable = true; + } + } +#endif +#endif + + // run each testcase with and without test enabled + QTest::addColumn("cache"); + QTest::newRow("WithCache") << true; + QTest::newRow("WithoutCache") << false; +} + +void tst_QHostInfo::init() +{ + // delete the cache so inidividual testcase results are independent from each other + qt_qhostinfo_clear_cache(); + + QFETCH_GLOBAL(bool, cache); + qt_qhostinfo_enable_cache(cache); +} + +void tst_QHostInfo::cleanup() +{ +} + +void tst_QHostInfo::lookupIPv4_data() +{ + QTest::addColumn("hostname"); + QTest::addColumn("addresses"); + QTest::addColumn("err"); + + // Test server lookup + QTest::newRow("lookup_01") << QtNetworkSettings::serverName() << QtNetworkSettings::serverIP().toString() << int(QHostInfo::NoError); + QTest::newRow("empty") << "" << "" << int(QHostInfo::HostNotFound); + + QTest::newRow("single_ip4") << "lupinella.troll.no" << lupinellaIp << int(QHostInfo::NoError); + QTest::newRow("multiple_ip4") << "multi.dev.troll.no" << "1.2.3.4 1.2.3.5 10.3.3.31" << int(QHostInfo::NoError); + QTest::newRow("literal_ip4") << lupinellaIp << lupinellaIp << int(QHostInfo::NoError); + + QTest::newRow("notfound") << "this-name-does-not-exist-hopefully." << "" << int(QHostInfo::HostNotFound); + + QTest::newRow("idn-ace") << "xn--alqualond-34a.troll.no" << "10.3.3.55" << int(QHostInfo::NoError); + QTest::newRow("idn-unicode") << QString::fromLatin1("alqualond\353.troll.no") << "10.3.3.55" << int(QHostInfo::NoError); +} + +void tst_QHostInfo::lookupIPv4() +{ + QFETCH(QString, hostname); + QFETCH(int, err); + QFETCH(QString, addresses); + + lookupDone = false; + QHostInfo::lookupHost(hostname, this, SLOT(resultsReady(const QHostInfo&))); + + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(lookupDone); + + if ((int)lookupResults.error() != (int)err) { + qWarning() << hostname << "=>" << lookupResults.errorString(); + } + QCOMPARE((int)lookupResults.error(), (int)err); + + QStringList tmp; + for (int i = 0; i < lookupResults.addresses().count(); ++i) + tmp.append(lookupResults.addresses().at(i).toString()); + tmp.sort(); + + QStringList expected = addresses.split(' '); + expected.sort(); + + QCOMPARE(tmp.join(" "), expected.join(" ")); +} + +void tst_QHostInfo::lookupIPv6_data() +{ + QTest::addColumn("hostname"); + QTest::addColumn("addresses"); + QTest::addColumn("err"); + + QTest::newRow("ipv6-net") << "www.ipv6-net.org" << "62.93.217.177 2001:618:1401::4" << int(QHostInfo::NoError); + QTest::newRow("ipv6-test") << "ipv6-test.dev.troll.no" << "2001:638:a00:2::2" << int(QHostInfo::NoError); + QTest::newRow("dns6-test") << "dns6-test-dev.troll.no" << "2001:470:1f01:115::10" << int(QHostInfo::NoError); + QTest::newRow("multi-dns6") << "multi-dns6-test-dev.troll.no" << "2001:470:1f01:115::11 2001:470:1f01:115::12" << int(QHostInfo::NoError); + QTest::newRow("dns46-test") << "dns46-test-dev.troll.no" << "10.3.4.90 2001:470:1f01:115::13" << int(QHostInfo::NoError); + + // avoid using real IPv6 addresses here because this will do a DNS query + // real addresses are between 2000:: and 3fff:ffff:ffff:ffff:ffff:ffff:ffff + QTest::newRow("literal_ip6") << "f001:6b0:1:ea:202:a5ff:fecd:13a6" << "f001:6b0:1:ea:202:a5ff:fecd:13a6" << int(QHostInfo::NoError); + QTest::newRow("literal_shortip6") << "f001:618:1401::4" << "f001:618:1401::4" << int(QHostInfo::NoError); +} + +void tst_QHostInfo::lookupIPv6() +{ + QFETCH(QString, hostname); + QFETCH(int, err); + QFETCH(QString, addresses); + + if (!ipv6LookupsAvailable) + QSKIP("This platform does not support IPv6 lookups", SkipAll); + + lookupDone = false; + QHostInfo::lookupHost(hostname, this, SLOT(resultsReady(const QHostInfo&))); + + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(lookupDone); + + QCOMPARE((int)lookupResults.error(), (int)err); + + QStringList tmp; + for (int i = 0; i < lookupResults.addresses().count(); ++i) + tmp.append(lookupResults.addresses().at(i).toString()); + tmp.sort(); + + QStringList expected = addresses.split(' '); + expected.sort(); + + QCOMPARE(tmp.join(" ").toLower(), expected.join(" ").toLower()); +} + +void tst_QHostInfo::reverseLookup_data() +{ + QTest::addColumn("address"); + QTest::addColumn("hostNames"); + QTest::addColumn("err"); + + QTest::newRow("trolltech.com") << QString("62.70.27.69") << QStringList(QString("diverse.troll.no")) << 0; + + // ### Use internal DNS instead. Discussed with Andreas. + //QTest::newRow("classical.hexago.com") << QString("2001:5c0:0:2::24") << QStringList(QString("classical.hexago.com")) << 0; + QTest::newRow("gitorious.org") << QString("87.238.52.168") << QStringList(QString("gitorious.org")) << 0; + QTest::newRow("bogus-name") << QString("1::2::3::4") << QStringList() << 1; +} + +void tst_QHostInfo::reverseLookup() +{ + QFETCH(QString, address); + QFETCH(QStringList, hostNames); + QFETCH(int, err); + + if (!ipv6LookupsAvailable && hostNames.contains("classical.hexago.com")) { + QSKIP("IPv6 lookups are not supported on this platform", SkipSingle); + } +#if defined(Q_OS_HPUX) && defined(__ia64) + if (hostNames.contains("classical.hexago.com")) + QSKIP("HP-UX 11i does not support IPv6 reverse lookups.", SkipSingle); +#endif + + QHostInfo info = QHostInfo::fromName(address); + + if (err == 0) { + QVERIFY(hostNames.contains(info.hostName())); + QCOMPARE(info.addresses().first(), QHostAddress(address)); + } else { + QCOMPARE(info.hostName(), address); + QCOMPARE(info.error(), QHostInfo::HostNotFound); + } + +} + +void tst_QHostInfo::blockingLookup_data() +{ + lookupIPv4_data(); + if (ipv6LookupsAvailable) + lookupIPv6_data(); +} + +void tst_QHostInfo::blockingLookup() +{ + QFETCH(QString, hostname); + QFETCH(int, err); + QFETCH(QString, addresses); + + QHostInfo hostInfo = QHostInfo::fromName(hostname); + QStringList tmp; + for (int i = 0; i < hostInfo.addresses().count(); ++i) + tmp.append(hostInfo.addresses().at(i).toString()); + tmp.sort(); + + if ((int)hostInfo.error() != (int)err) { + qWarning() << hostname << "=>" << lookupResults.errorString(); + } + QCOMPARE((int)hostInfo.error(), (int)err); + + QStringList expected = addresses.split(' '); + expected.sort(); + + QCOMPARE(tmp.join(" ").toUpper(), expected.join(" ").toUpper()); +} + +void tst_QHostInfo::raceCondition() +{ + for (int i = 0; i < 1000; ++i) { + QTcpSocket socket; + socket.connectToHost("notavalidname.troll.no", 80); + } +} + +class LookupThread : public QThread +{ +protected: + inline void run() + { + QHostInfo info = QHostInfo::fromName("qt.nokia.com"); + QCOMPARE(info.error(), QHostInfo::NoError); + QVERIFY(info.addresses().count() > 0); + QCOMPARE(info.addresses().at(0).toString(), QString("87.238.50.178")); + } +}; + +void tst_QHostInfo::threadSafety() +{ + const int nattempts = 5; +#if defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN) + const int runs = 10; +#else + const int runs = 100; +#endif + LookupThread thr[nattempts]; + for (int j = 0; j < runs; ++j) { + for (int i = 0; i < nattempts; ++i) + thr[i].start(); + for (int k = nattempts - 1; k >= 0; --k) + thr[k].wait(); + } +} + +class LookupReceiver : public QObject +{ + Q_OBJECT +public slots: + void start(); + void resultsReady(const QHostInfo&); +public: + QHostInfo result; + int numrequests; +}; + +void LookupReceiver::start() +{ + for (int i=0;iquit(); +} + +void tst_QHostInfo::threadSafetyAsynchronousAPI() +{ + const int nattempts = 10; + const int lookupsperthread = 10; + QList threads; + QList receivers; + for (int i = 0; i < nattempts; ++i) { + QThread* thread = new QThread; + LookupReceiver* receiver = new LookupReceiver; + receiver->numrequests = lookupsperthread; + receivers.append(receiver); + receiver->moveToThread(thread); + connect(thread, SIGNAL(started()), receiver, SLOT(start())); + thread->start(); + threads.append(thread); + } + for (int k = threads.count() - 1; k >= 0; --k) + QVERIFY(threads.at(k)->wait(60000)); + foreach (LookupReceiver* receiver, receivers) { + QCOMPARE(receiver->result.error(), QHostInfo::NoError); + QCOMPARE(receiver->result.addresses().at(0).toString(), QString("87.238.50.178")); + QCOMPARE(receiver->numrequests, 0); + } +} + +// this test is for the multi-threaded QHostInfo rewrite. It is about getting results at all, +// not about getting correct IPs +void tst_QHostInfo::multipleSameLookups() +{ + const int COUNT = 10; + lookupsDoneCounter = 0; + + for (int i = 0; i < COUNT; i++) + QHostInfo::lookupHost("localhost", this, SLOT(resultsReady(const QHostInfo))); + + QElapsedTimer timer; + timer.start(); + while (timer.elapsed() < 10000 && lookupsDoneCounter < COUNT) { + QTestEventLoop::instance().enterLoop(2); + } + QCOMPARE(lookupsDoneCounter, COUNT); +} + +// this test is for the multi-threaded QHostInfo rewrite. It is about getting results at all, +// not about getting correct IPs +void tst_QHostInfo::multipleDifferentLookups_data() +{ + QTest::addColumn("repeats"); + QTest::newRow("1") << 1; + QTest::newRow("2") << 2; + QTest::newRow("5") << 5; + QTest::newRow("10") << 10; +} + +void tst_QHostInfo::multipleDifferentLookups() +{ + QStringList hostnameList; + hostnameList << "www.ovi.com" << "www.nokia.com" << "qt.nokia.com" << "www.trolltech.com" << "troll.no" + << "www.qtcentre.org" << "forum.nokia.com" << "www.nokia.com" << "wiki.forum.nokia.com" + << "www.nokia.com" << "nokia.de" << "127.0.0.1" << "----"; + + QFETCH(int, repeats); + const int COUNT = hostnameList.size(); + lookupsDoneCounter = 0; + + for (int i = 0; i < hostnameList.size(); i++) + for (int j = 0; j < repeats; ++j) + QHostInfo::lookupHost(hostnameList.at(i), this, SLOT(resultsReady(const QHostInfo))); + + QElapsedTimer timer; + timer.start(); + while (timer.elapsed() < 60000 && lookupsDoneCounter < repeats*COUNT) { + QTestEventLoop::instance().enterLoop(2); + //qDebug() << "t:" << timer.elapsed(); + } + QCOMPARE(lookupsDoneCounter, repeats*COUNT); +} + +void tst_QHostInfo::cache() +{ + QFETCH_GLOBAL(bool, cache); + if (!cache) + return; // test makes only sense when cache enabled + + // reset slot counter + lookupsDoneCounter = 0; + + // lookup once, wait in event loop, result should not come directly. + bool valid = true; + int id = -1; + QHostInfo result = qt_qhostinfo_lookup("localhost", this, SLOT(resultsReady(QHostInfo)), &valid, &id); + QTestEventLoop::instance().enterLoop(5); + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(valid == false); + QVERIFY(result.addresses().isEmpty()); + + // loopkup second time, result should come directly + valid = false; + result = qt_qhostinfo_lookup("localhost", this, SLOT(resultsReady(QHostInfo)), &valid, &id); + QVERIFY(valid == true); + QVERIFY(!result.addresses().isEmpty()); + + // clear the cache + qt_qhostinfo_clear_cache(); + + // lookup third time, result should not come directly. + valid = true; + result = qt_qhostinfo_lookup("localhost", this, SLOT(resultsReady(QHostInfo)), &valid, &id); + QTestEventLoop::instance().enterLoop(5); + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(valid == false); + QVERIFY(result.addresses().isEmpty()); + + // the slot should have been called 2 times. + QCOMPARE(lookupsDoneCounter, 2); +} + +void tst_QHostInfo::resultsReady(const QHostInfo &hi) +{ + lookupDone = true; + lookupResults = hi; + lookupsDoneCounter++; + QTestEventLoop::instance().exitLoop(); +} + +void tst_QHostInfo::abortHostLookup() +{ + //reset counter + lookupsDoneCounter = 0; + bool valid = false; + int id = -1; + QHostInfo result = qt_qhostinfo_lookup("qt.nokia.com", this, SLOT(resultsReady(QHostInfo)), &valid, &id); + QVERIFY(!valid); + //it is assumed that the DNS request/response in the backend is slower than it takes to call abort + QHostInfo::abortHostLookup(id); + QTestEventLoop::instance().enterLoop(5); + QCOMPARE(lookupsDoneCounter, 0); +} + +class LookupAborter : public QObject +{ + Q_OBJECT +public slots: + void abort() + { + QHostInfo::abortHostLookup(id); + QThread::currentThread()->quit(); + } +public: + int id; +}; + +void tst_QHostInfo::abortHostLookupInDifferentThread() +{ + //reset counter + lookupsDoneCounter = 0; + bool valid = false; + int id = -1; + QHostInfo result = qt_qhostinfo_lookup("qt.nokia.com", this, SLOT(resultsReady(QHostInfo)), &valid, &id); + QVERIFY(!valid); + QThread thread; + LookupAborter aborter; + aborter.id = id; + aborter.moveToThread(&thread); + connect(&thread, SIGNAL(started()), &aborter, SLOT(abort())); + //it is assumed that the DNS request/response in the backend is slower than it takes to schedule the thread and call abort + thread.start(); + QVERIFY(thread.wait(5000)); + QTestEventLoop::instance().enterLoop(5); + QCOMPARE(lookupsDoneCounter, 0); +} + +QTEST_MAIN(tst_QHostInfo) +#include "tst_qhostinfo.moc" diff --git a/tests/auto/network/kernel/qnetworkaddressentry/.gitignore b/tests/auto/network/kernel/qnetworkaddressentry/.gitignore new file mode 100644 index 0000000000..375dbcfd0f --- /dev/null +++ b/tests/auto/network/kernel/qnetworkaddressentry/.gitignore @@ -0,0 +1 @@ +tst_qnetworkaddressentry diff --git a/tests/auto/network/kernel/qnetworkaddressentry/qnetworkaddressentry.pro b/tests/auto/network/kernel/qnetworkaddressentry/qnetworkaddressentry.pro new file mode 100644 index 0000000000..885dbf796c --- /dev/null +++ b/tests/auto/network/kernel/qnetworkaddressentry/qnetworkaddressentry.pro @@ -0,0 +1,6 @@ +load(qttest_p4) +SOURCES += tst_qnetworkaddressentry.cpp + +QT = core network + +symbian: TARGET.CAPABILITY = NetworkServices diff --git a/tests/auto/network/kernel/qnetworkaddressentry/tst_qnetworkaddressentry.cpp b/tests/auto/network/kernel/qnetworkaddressentry/tst_qnetworkaddressentry.cpp new file mode 100644 index 0000000000..42b9af8dd3 --- /dev/null +++ b/tests/auto/network/kernel/qnetworkaddressentry/tst_qnetworkaddressentry.cpp @@ -0,0 +1,185 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include +#include + +// TESTED_FILES=qnetworkinterface.cpp qnetworkinterface.h +Q_DECLARE_METATYPE(QHostAddress) + +class tst_QNetworkAddressEntry: public QObject +{ + Q_OBJECT +private slots: + void getSetCheck(); + void prefixAndNetmask_data(); + void prefixAndNetmask(); +}; + +void tst_QNetworkAddressEntry::getSetCheck() +{ + QNetworkAddressEntry entry; + + QVERIFY(entry.ip().isNull()); + QVERIFY(entry.netmask().isNull()); + QVERIFY(entry.broadcast().isNull()); + QCOMPARE(entry.prefixLength(), -1); + + entry.setIp(QHostAddress::LocalHost); + QCOMPARE(entry.ip(), QHostAddress(QHostAddress::LocalHost)); + entry.setIp(QHostAddress()); + QVERIFY(entry.ip().isNull()); + + entry.setBroadcast(QHostAddress::LocalHost); + QCOMPARE(entry.broadcast(), QHostAddress(QHostAddress::LocalHost)); + entry.setBroadcast(QHostAddress()); + QVERIFY(entry.broadcast().isNull()); + + // netmask and prefix length tested in the next test + entry.setIp(QHostAddress::LocalHost); + entry.setBroadcast(QHostAddress::LocalHost); + + QNetworkAddressEntry entry2; + QVERIFY(entry != entry2); + QVERIFY(!(entry == entry2)); + + entry = entry2; + QCOMPARE(entry, entry2); + QVERIFY(entry == entry); + QVERIFY(!(entry != entry2)); +} + +void tst_QNetworkAddressEntry::prefixAndNetmask_data() +{ + QTest::addColumn("ip"); + QTest::addColumn("netmask"); + QTest::addColumn("prefix"); + + // IPv4 set: + QHostAddress ipv4(QHostAddress::LocalHost); + QTest::newRow("v4/0") << ipv4 << QHostAddress(QHostAddress::AnyIPv4) << 0; + QTest::newRow("v4/32") << ipv4 << QHostAddress("255.255.255.255") << 32; + QTest::newRow("v4/24") << ipv4 << QHostAddress("255.255.255.0") << 24; + QTest::newRow("v4/23") << ipv4 << QHostAddress("255.255.254.0") << 23; + QTest::newRow("v4/20") << ipv4 << QHostAddress("255.255.240.0") << 20; + QTest::newRow("v4/invalid1") << ipv4 << QHostAddress(QHostAddress::LocalHost) << -1; + QTest::newRow("v4/invalid2") << ipv4 << QHostAddress(QHostAddress::AnyIPv6) << -1; + QTest::newRow("v4/invalid3") << ipv4 << QHostAddress("255.255.253.0") << -1; + QTest::newRow("v4/invalid4") << ipv4 << QHostAddress() << -2; + QTest::newRow("v4/invalid5") << ipv4 << QHostAddress() << 33; + + // IPv6 set: + QHostAddress ipv6(QHostAddress::LocalHostIPv6); + QTest::newRow("v6/0") << ipv6 << QHostAddress(QHostAddress::AnyIPv6) << 0; + QTest::newRow("v6/128") << ipv6 << QHostAddress("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") << 128; + QTest::newRow("v6/64") << ipv6 << QHostAddress("ffff:ffff:ffff:ffff::") << 64; + QTest::newRow("v6/63") << ipv6 << QHostAddress("ffff:ffff:ffff:fffe::") << 63; + QTest::newRow("v6/60") << ipv6 << QHostAddress("ffff:ffff:ffff:fff0::") << 60; + QTest::newRow("v6/48") << ipv6 << QHostAddress("ffff:ffff:ffff::") << 48; + QTest::newRow("v6/3") << ipv6 << QHostAddress("e000::") << 3; + QTest::newRow("v6/invalid1") << ipv6 << QHostAddress(QHostAddress::LocalHostIPv6) << -1; + QTest::newRow("v6/invalid2") << ipv6 << QHostAddress(QHostAddress::Any) << -1; + QTest::newRow("v6/invalid3") << ipv6 << QHostAddress("fffd::") << -1; + QTest::newRow("v6/invalid4") << ipv6 << QHostAddress() << -2; + QTest::newRow("v6/invalid5") << ipv6 << QHostAddress() << 129; +} + +void tst_QNetworkAddressEntry::prefixAndNetmask() +{ + QFETCH(QHostAddress, ip); + QFETCH(QHostAddress, netmask); + QFETCH(int, prefix); + + QNetworkAddressEntry entry; + + // first, without setting the IP, all must be invalid: + entry.setNetmask(netmask); + QVERIFY(entry.netmask().isNull()); + entry.setPrefixLength(prefix); + QCOMPARE(entry.prefixLength(), -1); + + // set the IP: + entry.setIp(ip); + + // set the netmask: + if (!netmask.isNull()) { + entry.setNetmask(netmask); + + // was it a valid one? + if (prefix != -1) { + QVERIFY(!entry.netmask().isNull()); + QCOMPARE(entry.netmask(), netmask); + QCOMPARE(entry.prefixLength(), prefix); + } else { + // not valid + QVERIFY(entry.netmask().isNull()); + QCOMPARE(entry.prefixLength(), -1); + } + } + entry.setNetmask(QHostAddress()); + QVERIFY(entry.netmask().isNull()); + QCOMPARE(entry.prefixLength(), -1); + + // set the prefix + if (prefix != -1) { + entry.setPrefixLength(prefix); + + // was it a valid one? + if (!netmask.isNull()) { + QVERIFY(!entry.netmask().isNull()); + QCOMPARE(entry.netmask(), netmask); + QCOMPARE(entry.prefixLength(), prefix); + } else { + // not valid + QVERIFY(entry.netmask().isNull()); + QCOMPARE(entry.prefixLength(), -1); + } + } + entry.setPrefixLength(-1); + QVERIFY(entry.netmask().isNull()); + QCOMPARE(entry.prefixLength(), -1); +} + +QTEST_MAIN(tst_QNetworkAddressEntry) +#include "tst_qnetworkaddressentry.moc" + diff --git a/tests/auto/network/kernel/qnetworkinterface/.gitignore b/tests/auto/network/kernel/qnetworkinterface/.gitignore new file mode 100644 index 0000000000..237bc6d0db --- /dev/null +++ b/tests/auto/network/kernel/qnetworkinterface/.gitignore @@ -0,0 +1 @@ +tst_qnetworkinterface diff --git a/tests/auto/network/kernel/qnetworkinterface/qnetworkinterface.pro b/tests/auto/network/kernel/qnetworkinterface/qnetworkinterface.pro new file mode 100644 index 0000000000..1c5feee3f2 --- /dev/null +++ b/tests/auto/network/kernel/qnetworkinterface/qnetworkinterface.pro @@ -0,0 +1,7 @@ +load(qttest_p4) +SOURCES += tst_qnetworkinterface.cpp + +QT = core network + +symbian: TARGET.CAPABILITY = NetworkServices + diff --git a/tests/auto/network/kernel/qnetworkinterface/tst_qnetworkinterface.cpp b/tests/auto/network/kernel/qnetworkinterface/tst_qnetworkinterface.cpp new file mode 100644 index 0000000000..7a2f0e578e --- /dev/null +++ b/tests/auto/network/kernel/qnetworkinterface/tst_qnetworkinterface.cpp @@ -0,0 +1,241 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include + +#include +#include +#include +#include +#include +#include "../../../network-settings.h" + +//TESTED_FILES=qnetworkinterface.cpp qnetworkinterface.h qnetworkinterface_unix.cpp qnetworkinterface_win.cpp + +class tst_QNetworkInterface : public QObject +{ + Q_OBJECT + +public: + tst_QNetworkInterface(); + virtual ~tst_QNetworkInterface(); + +private slots: + void initTestCase(); + void cleanupTestCase(); + void dump(); + void loopbackIPv4(); + void loopbackIPv6(); + void localAddress(); + void interfaceFromXXX(); + void copyInvalidInterface(); + +private: +#ifndef QT_NO_BEARER_MANAGEMENT + QNetworkConfigurationManager *netConfMan; + QNetworkConfiguration networkConfiguration; + QScopedPointer networkSession; +#endif +}; + +tst_QNetworkInterface::tst_QNetworkInterface() +{ +} + +tst_QNetworkInterface::~tst_QNetworkInterface() +{ +} + +void tst_QNetworkInterface::initTestCase() +{ +#ifndef QT_NO_BEARERMANAGEMENT + netConfMan = new QNetworkConfigurationManager(this); + networkConfiguration = netConfMan->defaultConfiguration(); + networkSession.reset(new QNetworkSession(networkConfiguration)); + if (!networkSession->isOpen()) { + networkSession->open(); + QVERIFY(networkSession->waitForOpened(30000)); + } +#endif +} + +void tst_QNetworkInterface::cleanupTestCase() +{ +#ifndef QT_NO_BEARERMANAGEMENT + if (networkSession && networkSession->isOpen()) { + networkSession->close(); + } +#endif +} + +void tst_QNetworkInterface::dump() +{ + // This is for manual testing: + QList allInterfaces = QNetworkInterface::allInterfaces(); + foreach (const QNetworkInterface &i, allInterfaces) { + QString flags; + if (i.flags() & QNetworkInterface::IsUp) flags += "Up,"; + if (i.flags() & QNetworkInterface::IsRunning) flags += "Running,"; + if (i.flags() & QNetworkInterface::CanBroadcast) flags += "Broadcast,"; + if (i.flags() & QNetworkInterface::IsLoopBack) flags += "Loopback,"; + if (i.flags() & QNetworkInterface::IsPointToPoint) flags += "PointToPoint,"; + if (i.flags() & QNetworkInterface::CanMulticast) flags += "Multicast,"; + flags.chop(1); // drop last comma + + QString friendlyName = i.humanReadableName(); + if (friendlyName != i.name()) { + friendlyName.prepend('('); + friendlyName.append(')'); + } else { + friendlyName.clear(); + } + qDebug() << "Interface: " << i.name() << qPrintable(friendlyName); + QVERIFY(i.isValid()); + + qDebug() << " index: " << i.index(); + qDebug() << " flags: " << qPrintable(flags); + qDebug() << " hw address:" << qPrintable(i.hardwareAddress()); + + int count = 0; + foreach (const QNetworkAddressEntry &e, i.addressEntries()) { + QDebug s = qDebug(); + s.nospace() << " address " + << qSetFieldWidth(2) << count++ << qSetFieldWidth(0); + s.nospace() << ": " << qPrintable(e.ip().toString()); + if (!e.netmask().isNull()) + s.nospace() << '/' << e.prefixLength() + << " (" << qPrintable(e.netmask().toString()) << ")"; + if (!e.broadcast().isNull()) + s.nospace() << " broadcast " << qPrintable(e.broadcast().toString()); + } + } +} + +void tst_QNetworkInterface::loopbackIPv4() +{ + QList all = QNetworkInterface::allAddresses(); + QVERIFY(all.contains(QHostAddress(QHostAddress::LocalHost))); +} + +void tst_QNetworkInterface::loopbackIPv6() +{ + QList all = QNetworkInterface::allAddresses(); + + bool loopbackfound = false; + bool anyIPv6 = false; + foreach (QHostAddress addr, all) + if (addr == QHostAddress::LocalHostIPv6) { + loopbackfound = true; + anyIPv6 = true; + break; + } else if (addr.protocol() == QAbstractSocket::IPv6Protocol) + anyIPv6 = true; + + QVERIFY(!anyIPv6 || loopbackfound); +} + +void tst_QNetworkInterface::localAddress() +{ + QTcpSocket socket; + socket.connectToHost(QtNetworkSettings::serverName(), 80); + QVERIFY(socket.waitForConnected(5000)); + + QHostAddress local = socket.localAddress(); + + // make Apache happy on fluke + socket.write("GET / HTTP/1.0\r\n\r\n"); + socket.waitForBytesWritten(1000); + socket.close(); + + // test that we can find the address that QTcpSocket reported + QList all = QNetworkInterface::allAddresses(); + QVERIFY(all.contains(local)); +} + +void tst_QNetworkInterface::interfaceFromXXX() +{ + QList allInterfaces = QNetworkInterface::allInterfaces(); + + foreach (QNetworkInterface iface, allInterfaces) { + QVERIFY(QNetworkInterface::interfaceFromName(iface.name()).isValid()); + foreach (QNetworkAddressEntry entry, iface.addressEntries()) { + QVERIFY(!entry.ip().isNull()); + + if (!entry.netmask().isNull()) { + QCOMPARE(entry.netmask().protocol(), entry.ip().protocol()); + + // if the netmask is known, the broadcast is known + // but only for IPv4 (there is no such thing as broadcast in IPv6) + if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol) { + QVERIFY(!entry.broadcast().isNull()); + + // verify that the broadcast address is correct + quint32 ip = entry.ip().toIPv4Address(); + quint32 mask = entry.netmask().toIPv4Address(); + quint32 bcast = entry.broadcast().toIPv4Address(); + + QCOMPARE(bcast, ip | ~mask); + } + } + + if (!entry.broadcast().isNull()) + QCOMPARE(entry.broadcast().protocol(), entry.ip().protocol()); + } + } +} + +void tst_QNetworkInterface::copyInvalidInterface() +{ + // Force a copy of an interfaces that isn't likely to exist + QNetworkInterface i = QNetworkInterface::interfaceFromName("plopp"); + QVERIFY(!i.isValid()); + + QCOMPARE(i.index(), 0); + QVERIFY(i.name().isEmpty()); + QVERIFY(i.humanReadableName().isEmpty()); + QVERIFY(i.hardwareAddress().isEmpty()); + QCOMPARE(int(i.flags()), 0); + QVERIFY(i.addressEntries().isEmpty()); +} + +QTEST_MAIN(tst_QNetworkInterface) +#include "tst_qnetworkinterface.moc" diff --git a/tests/auto/network/kernel/qnetworkproxy/.gitignore b/tests/auto/network/kernel/qnetworkproxy/.gitignore new file mode 100644 index 0000000000..74dcafa759 --- /dev/null +++ b/tests/auto/network/kernel/qnetworkproxy/.gitignore @@ -0,0 +1 @@ +tst_qnetworkproxy diff --git a/tests/auto/network/kernel/qnetworkproxy/qnetworkproxy.pro b/tests/auto/network/kernel/qnetworkproxy/qnetworkproxy.pro new file mode 100644 index 0000000000..fc0a216a15 --- /dev/null +++ b/tests/auto/network/kernel/qnetworkproxy/qnetworkproxy.pro @@ -0,0 +1,11 @@ +############################################################ +# Project file for autotest for file qnetworkproxy.h +############################################################ + +load(qttest_p4) +QT = core network + +SOURCES += tst_qnetworkproxy.cpp + +symbian: TARGET.CAPABILITY = NetworkServices + diff --git a/tests/auto/network/kernel/qnetworkproxy/tst_qnetworkproxy.cpp b/tests/auto/network/kernel/qnetworkproxy/tst_qnetworkproxy.cpp new file mode 100644 index 0000000000..884ed0573f --- /dev/null +++ b/tests/auto/network/kernel/qnetworkproxy/tst_qnetworkproxy.cpp @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include + +#include +#include +#include + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QNetworkProxy : public QObject +{ +Q_OBJECT + +public: + tst_QNetworkProxy(); + virtual ~tst_QNetworkProxy(); + +private slots: + void getSetCheck(); + void capabilitiesPerType(); +}; + +tst_QNetworkProxy::tst_QNetworkProxy() +{ +} + +tst_QNetworkProxy::~tst_QNetworkProxy() +{ +} + +// Testing get/set functions +void tst_QNetworkProxy::getSetCheck() +{ + QNetworkProxy obj1; + // quint16 QNetworkProxy::port() + // void QNetworkProxy::setPort(quint16) + obj1.setPort(quint16(0)); + QCOMPARE(quint16(0), obj1.port()); + obj1.setPort(quint16(0xffff)); + QCOMPARE(quint16(0xffff), obj1.port()); + + obj1.setType(QNetworkProxy::DefaultProxy); + QCOMPARE(obj1.type(), QNetworkProxy::DefaultProxy); + obj1.setType(QNetworkProxy::HttpProxy); + QCOMPARE(obj1.type(), QNetworkProxy::HttpProxy); + obj1.setType(QNetworkProxy::Socks5Proxy); + QCOMPARE(obj1.type(), QNetworkProxy::Socks5Proxy); +} + +void tst_QNetworkProxy::capabilitiesPerType() +{ + QNetworkProxy proxy(QNetworkProxy::Socks5Proxy); + QVERIFY(proxy.capabilities() & QNetworkProxy::TunnelingCapability); + QVERIFY(proxy.capabilities() & QNetworkProxy::HostNameLookupCapability); + QVERIFY(proxy.capabilities() & QNetworkProxy::UdpTunnelingCapability); + + proxy.setType(QNetworkProxy::NoProxy); + // verify that the capabilities changed + QVERIFY(!(proxy.capabilities() & QNetworkProxy::HostNameLookupCapability)); + QVERIFY(proxy.capabilities() & QNetworkProxy::UdpTunnelingCapability); + + proxy.setType(QNetworkProxy::HttpProxy); + QVERIFY(proxy.capabilities() & QNetworkProxy::HostNameLookupCapability); + QVERIFY(!(proxy.capabilities() & QNetworkProxy::UdpTunnelingCapability)); + + // now set the capabilities on stone: + proxy.setCapabilities(QNetworkProxy::TunnelingCapability | QNetworkProxy::UdpTunnelingCapability); + QCOMPARE(proxy.capabilities(), QNetworkProxy::TunnelingCapability | QNetworkProxy::UdpTunnelingCapability); + + // changing the type shouldn't change the capabilities any more + proxy.setType(QNetworkProxy::Socks5Proxy); + QCOMPARE(proxy.capabilities(), QNetworkProxy::TunnelingCapability | QNetworkProxy::UdpTunnelingCapability); +} + +QTEST_MAIN(tst_QNetworkProxy) +#include "tst_qnetworkproxy.moc" diff --git a/tests/auto/network/kernel/qnetworkproxyfactory/.gitignore b/tests/auto/network/kernel/qnetworkproxyfactory/.gitignore new file mode 100644 index 0000000000..9be26bbda1 --- /dev/null +++ b/tests/auto/network/kernel/qnetworkproxyfactory/.gitignore @@ -0,0 +1 @@ +tst_qnetworkproxyfactory_symbian diff --git a/tests/auto/network/kernel/qnetworkproxyfactory/qnetworkproxyfactory.pro b/tests/auto/network/kernel/qnetworkproxyfactory/qnetworkproxyfactory.pro new file mode 100644 index 0000000000..17ad403ba7 --- /dev/null +++ b/tests/auto/network/kernel/qnetworkproxyfactory/qnetworkproxyfactory.pro @@ -0,0 +1,11 @@ +############################################################ +# Project file for autotest for file qnetworkproxy.h (proxy factory part) +############################################################ + +load(qttest_p4) +QT = core network + +SOURCES += tst_qnetworkproxyfactory.cpp + +symbian: TARGET.CAPABILITY = NetworkServices ReadUserData + diff --git a/tests/auto/network/kernel/qnetworkproxyfactory/tst_qnetworkproxyfactory.cpp b/tests/auto/network/kernel/qnetworkproxyfactory/tst_qnetworkproxyfactory.cpp new file mode 100644 index 0000000000..1df6c24c10 --- /dev/null +++ b/tests/auto/network/kernel/qnetworkproxyfactory/tst_qnetworkproxyfactory.cpp @@ -0,0 +1,275 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QNetworkConfiguration); +Q_DECLARE_METATYPE(QList); + +#include + +class tst_QNetworkProxyFactory : public QObject { + Q_OBJECT + +public: + tst_QNetworkProxyFactory(); + + class QDebugProxyFactory : public QNetworkProxyFactory + { + public: + virtual QList queryProxy(const QNetworkProxyQuery &query = QNetworkProxyQuery()) + { + returnedList = QNetworkProxyFactory::systemProxyForQuery(query); + requestCounter++; + return returnedList; + } + QList returnedList; + int requestCounter; + }; + +private slots: + void systemProxyForQueryCalledFromThread(); + void systemProxyForQuery() const; +#ifndef QT_NO_BEARERMANAGEMENT + void fromConfigurations(); + void inNetworkAccessManager_data(); + void inNetworkAccessManager(); +#endif + +private: + QString formatProxyName(const QNetworkProxy & proxy) const; + QDebugProxyFactory *factory; +}; + +tst_QNetworkProxyFactory::tst_QNetworkProxyFactory() +{ + factory = new QDebugProxyFactory; + QNetworkProxyFactory::setApplicationProxyFactory(factory); +} + +QString tst_QNetworkProxyFactory::formatProxyName(const QNetworkProxy & proxy) const +{ + QString proxyName; + if (!proxy.user().isNull()) + proxyName.append("%1:%2@").arg(proxy.user(), proxy.password()); + proxyName.append(QString("%1:%2").arg(proxy.hostName()).arg(proxy.port())); + proxyName.append(QString(" (type=%1, capabilities=%2)").arg(proxy.type()).arg(proxy.capabilities())); + + return proxyName; +} + +void tst_QNetworkProxyFactory::systemProxyForQuery() const +{ + QNetworkProxyQuery query(QUrl(QString("http://www.abc.com")), QNetworkProxyQuery::UrlRequest); + QList systemProxyList = QNetworkProxyFactory::systemProxyForQuery(query); + bool pass = true; + QNetworkProxy proxy; + + QList nativeProxyList; + nativeProxyList << QNetworkProxy(QNetworkProxy::HttpProxy, QString("http://test.proxy.com"), 8080) << QNetworkProxy::NoProxy; + + foreach (proxy, systemProxyList) { + if (!nativeProxyList.contains(proxy)) { + qWarning() << "System proxy not found in native proxy list: " << + formatProxyName(proxy); + pass = false; + } + } + + foreach (proxy, nativeProxyList) { + if (!systemProxyList.contains(proxy)) { + qWarning() << "Native proxy not found in system proxy list: " << + formatProxyName(proxy); + pass = false; + } + } + + if (!pass) + QFAIL("One or more system proxy lookup failures occurred."); +} + +#ifndef QT_NO_BEARERMANAGEMENT + +//Purpose of this test is just to check systemProxyForQuery doesn't hang or crash +//with any given configuration including no configuration. +//We can't test it returns the right proxies without implementing the native proxy code +//again here, which would be testing our implementation against itself. +//Therefore it's just testing that something valid is returned (at least a NoProxy entry) +void tst_QNetworkProxyFactory::fromConfigurations() +{ + QNetworkConfigurationManager manager; + QList proxies; + QUrl url(QLatin1String("http://qt.nokia.com")); + //get from known configurations + foreach (QNetworkConfiguration config, manager.allConfigurations()) { + QNetworkProxyQuery query(config, url, QNetworkProxyQuery::UrlRequest); + proxies = QNetworkProxyFactory::systemProxyForQuery(query); + QVERIFY(!proxies.isEmpty()); + foreach (QNetworkProxy proxy, proxies) { + qDebug() << config.name() << " - " << config.identifier() << " - " << formatProxyName(proxy); + } + } + + //get from default configuration + QNetworkProxyQuery defaultquery(url, QNetworkProxyQuery::UrlRequest); + proxies = QNetworkProxyFactory::systemProxyForQuery(defaultquery); + QVERIFY(!proxies.isEmpty()); + foreach (QNetworkProxy proxy, proxies) { + qDebug() << "default - " << formatProxyName(proxy); + } + + //get from active configuration + QNetworkSession session(manager.defaultConfiguration()); + session.open(); + QVERIFY(session.waitForOpened(30000)); + proxies = QNetworkProxyFactory::systemProxyForQuery(defaultquery); + QVERIFY(!proxies.isEmpty()); + foreach (QNetworkProxy proxy, proxies) { + qDebug() << "active - " << formatProxyName(proxy); + } + + //get from known configurations while there is one active + foreach (QNetworkConfiguration config, manager.allConfigurations()) { + QNetworkProxyQuery query(config, url, QNetworkProxyQuery::UrlRequest); + proxies = QNetworkProxyFactory::systemProxyForQuery(query); + QVERIFY(!proxies.isEmpty()); + foreach (QNetworkProxy proxy, proxies) { + qDebug() << config.name() << " - " << config.identifier() << " - " << formatProxyName(proxy); + } + } +} + +void tst_QNetworkProxyFactory::inNetworkAccessManager_data() +{ + QTest::addColumn("config"); + QTest::addColumn >("proxies"); + QNetworkConfigurationManager manager; + //get from known configurations + foreach (QNetworkConfiguration config, manager.allConfigurations()) { + QNetworkProxyQuery query(config, QUrl(QString("http://qt.nokia.com")), QNetworkProxyQuery::UrlRequest); + QList proxies = QNetworkProxyFactory::systemProxyForQuery(query); + QTest::newRow(config.name().toUtf8()) << config << proxies; + } +} + +//Purpose of this test is to check that QNetworkAccessManager uses the proxy from the configuration it +//has been given. Needs two or more working configurations to be a good test. +void tst_QNetworkProxyFactory::inNetworkAccessManager() +{ + QFETCH(QNetworkConfiguration, config); + QFETCH(QList, proxies); + + int count = factory->requestCounter; + + QNetworkAccessManager manager; + manager.setConfiguration(config); + + //using an internet server, because cellular APs won't have a route to the test server. + QNetworkRequest req(QUrl(QString("http://qt.nokia.com"))); + QNetworkReply *reply = manager.get(req); + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(30); + delete reply; + + if (count == factory->requestCounter) { + //RND phones are preconfigured with several test access points which won't work without a matching SIM + //If the network fails to start, QNAM won't ask the factory for proxies so we can't test. + QSKIP("network configuration didn't start", SkipSingle); + } + + qDebug() << "testing network configuration for" << config.name(); + foreach (QNetworkProxy proxy, factory->returnedList) { + qDebug() << formatProxyName(proxy); + } + qDebug() << " "; + foreach (QNetworkProxy proxy, proxies) { + qDebug() << formatProxyName(proxy); + } + if (config.type() != QNetworkConfiguration::InternetAccessPoint) + QEXPECT_FAIL("","QNetworkProxyFactory::systemProxyForQuery doesn't work for service networks yet", Continue); + QCOMPARE(factory->returnedList, proxies); +} + +#endif //QT_NO_BEARERMANAGEMENT + + +class QSPFQThread : public QThread +{ +protected: + virtual void run() + { + proxies = QNetworkProxyFactory::systemProxyForQuery(query); + } +public: + QNetworkProxyQuery query; + QList proxies; +}; + +//regression test for QTBUG-18799 +void tst_QNetworkProxyFactory::systemProxyForQueryCalledFromThread() +{ + QUrl url(QLatin1String("http://qt.nokia.com")); + QNetworkProxyQuery query(url); + QSPFQThread thread; + thread.query = query; + connect(&thread, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + thread.start(); + QTestEventLoop::instance().enterLoop(5); + QVERIFY(thread.isFinished()); + QCOMPARE(thread.proxies, QNetworkProxyFactory::systemProxyForQuery(query)); +} + +QTEST_MAIN(tst_QNetworkProxyFactory) +#include "tst_qnetworkproxyfactory.moc" diff --git a/tests/auto/network/network.pro b/tests/auto/network/network.pro new file mode 100644 index 0000000000..3eafd7df70 --- /dev/null +++ b/tests/auto/network/network.pro @@ -0,0 +1,8 @@ +TEMPLATE=subdirs +SUBDIRS=\ + access \ + bearer \ + kernel \ + ssl \ + socket \ + diff --git a/tests/auto/network/socket/platformsocketengine/.gitignore b/tests/auto/network/socket/platformsocketengine/.gitignore new file mode 100644 index 0000000000..afe93891ff --- /dev/null +++ b/tests/auto/network/socket/platformsocketengine/.gitignore @@ -0,0 +1 @@ +tst_platformsocketengine diff --git a/tests/auto/network/socket/platformsocketengine/platformsocketengine.pri b/tests/auto/network/socket/platformsocketengine/platformsocketengine.pri new file mode 100644 index 0000000000..15f31fdbb5 --- /dev/null +++ b/tests/auto/network/socket/platformsocketengine/platformsocketengine.pri @@ -0,0 +1,19 @@ +QT += network + +QNETWORK_SRC = $$QT_SOURCE_TREE/src/network + +INCLUDEPATH += $$QNETWORK_SRC + +win32 { + wince*: { + LIBS += -lws2 + } else { + LIBS += -lws2_32 + } +} + +unix:contains(QT_CONFIG, reduce_exports) { + SOURCES += $$QNETWORK_SRC/socket/qnativesocketengine_unix.cpp + SOURCES += $$QNETWORK_SRC/socket/qnativesocketengine.cpp + SOURCES += $$QNETWORK_SRC/socket/qabstractsocketengine.cpp +} diff --git a/tests/auto/network/socket/platformsocketengine/platformsocketengine.pro b/tests/auto/network/socket/platformsocketengine/platformsocketengine.pro new file mode 100644 index 0000000000..99ae358a93 --- /dev/null +++ b/tests/auto/network/socket/platformsocketengine/platformsocketengine.pro @@ -0,0 +1,16 @@ +load(qttest_p4) +SOURCES += tst_platformsocketengine.cpp + +include(../platformsocketengine/platformsocketengine.pri) + +requires(contains(QT_CONFIG,private_tests)) + +MOC_DIR=tmp + +QT = core-private network-private + +symbian { + TARGET.CAPABILITY = NetworkServices + INCLUDEPATH += $$OS_LAYER_SYSTEMINCLUDE + LIBS += -lesock +} diff --git a/tests/auto/network/socket/platformsocketengine/tst_platformsocketengine.cpp b/tests/auto/network/socket/platformsocketengine/tst_platformsocketengine.cpp new file mode 100644 index 0000000000..9e93108fc7 --- /dev/null +++ b/tests/auto/network/socket/platformsocketengine/tst_platformsocketengine.cpp @@ -0,0 +1,763 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include + +#ifdef Q_OS_WIN +#include +#endif + +#include + + +#include + +#include +#include + +#ifdef Q_OS_UNIX +#include +#include +#include +#endif + +#include + +#ifdef Q_OS_SYMBIAN +#include +#include +#include +#include +#define PLATFORMSOCKETENGINE QSymbianSocketEngine +#define PLATFORMSOCKETENGINESTRING "QSymbianSocketEngine" +#include +#include +#else +#define PLATFORMSOCKETENGINE QNativeSocketEngine +#define PLATFORMSOCKETENGINESTRING "QNativeSocketEngine" +#include +#endif + +#include + +#include "../../../network-settings.h" + +//TESTED_FILES=network/qnativesocketengine.cpp network/qnativesocketengine_p.h network/qnativesocketengine_unix.cpp + +class tst_PlatformSocketEngine : public QObject +{ + Q_OBJECT + +public: + tst_PlatformSocketEngine(); + virtual ~tst_PlatformSocketEngine(); + + +public slots: + void init(); + void cleanup(); +private slots: + void construction(); + void simpleConnectToIMAP(); + void udpLoopbackTest(); + void udpIPv6LoopbackTest(); + void broadcastTest(); + void serverTest(); + void udpLoopbackPerformance(); + void tcpLoopbackPerformance(); + void readWriteBufferSize(); + void bind(); + void networkError(); + void setSocketDescriptor(); + void invalidSend(); + void receiveUrgentData(); + void tooManySockets(); +}; + +tst_PlatformSocketEngine::tst_PlatformSocketEngine() +{ + Q_SET_DEFAULT_IAP +} + +tst_PlatformSocketEngine::~tst_PlatformSocketEngine() +{ +} + +void tst_PlatformSocketEngine::init() +{ +} + +void tst_PlatformSocketEngine::cleanup() +{ +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::construction() +{ + PLATFORMSOCKETENGINE socketDevice; + + QVERIFY(!socketDevice.isValid()); + + // Initialize device + QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + QVERIFY(socketDevice.isValid()); + QVERIFY(socketDevice.protocol() == QAbstractSocket::IPv4Protocol); + QVERIFY(socketDevice.socketType() == QAbstractSocket::TcpSocket); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); + QVERIFY(socketDevice.socketDescriptor() != -1); + QVERIFY(socketDevice.localAddress() == QHostAddress()); + QVERIFY(socketDevice.localPort() == 0); + QVERIFY(socketDevice.peerAddress() == QHostAddress()); + QVERIFY(socketDevice.peerPort() == 0); + QVERIFY(socketDevice.error() == QAbstractSocket::UnknownSocketError); + + QTest::ignoreMessage(QtWarningMsg, PLATFORMSOCKETENGINESTRING "::bytesAvailable() was called in QAbstractSocket::UnconnectedState"); + QVERIFY(socketDevice.bytesAvailable() == 0); + + QTest::ignoreMessage(QtWarningMsg, PLATFORMSOCKETENGINESTRING "::hasPendingDatagrams() was called in QAbstractSocket::UnconnectedState"); + QVERIFY(!socketDevice.hasPendingDatagrams()); +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::simpleConnectToIMAP() +{ + PLATFORMSOCKETENGINE socketDevice; + + // Initialize device + QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); + + const bool isConnected = socketDevice.connectToHost(QtNetworkSettings::serverIP(), 143); + if (!isConnected) { + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectingState); + QVERIFY(socketDevice.waitForWrite()); + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectedState); + } + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectedState); + QVERIFY(socketDevice.peerAddress() == QtNetworkSettings::serverIP()); + + // Wait for the greeting + QVERIFY(socketDevice.waitForRead()); + + // Read the greeting + qint64 available = socketDevice.bytesAvailable(); + QVERIFY(available > 0); + QByteArray array; + array.resize(available); + QVERIFY(socketDevice.read(array.data(), array.size()) == available); + + // Check that the greeting is what we expect it to be + QVERIFY2(QtNetworkSettings::compareReplyIMAP(array), array.constData()); + + // Write a logout message + QByteArray array2 = "ZZZ LOGOUT\r\n"; + QVERIFY(socketDevice.write(array2.data(), + array2.size()) == array2.size()); + + // Wait for the response + QVERIFY(socketDevice.waitForRead()); + + available = socketDevice.bytesAvailable(); + QVERIFY(available > 0); + array.resize(available); + QVERIFY(socketDevice.read(array.data(), array.size()) == available); + + // Check that the greeting is what we expect it to be + QCOMPARE(array.constData(), + "* BYE LOGOUT received\r\n" + "ZZZ OK Completed\r\n"); + + // Wait for the response + QVERIFY(socketDevice.waitForRead()); + char c; + QVERIFY(socketDevice.read(&c, sizeof(c)) == -1); + QVERIFY(socketDevice.error() == QAbstractSocket::RemoteHostClosedError); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::udpLoopbackTest() +{ + PLATFORMSOCKETENGINE udpSocket; + + // Initialize device #1 + QVERIFY(udpSocket.initialize(QAbstractSocket::UdpSocket)); + QVERIFY(udpSocket.isValid()); + QVERIFY(udpSocket.socketDescriptor() != -1); + QVERIFY(udpSocket.protocol() == QAbstractSocket::IPv4Protocol); + QVERIFY(udpSocket.socketType() == QAbstractSocket::UdpSocket); + QVERIFY(udpSocket.state() == QAbstractSocket::UnconnectedState); + + // Bind #1 to localhost + QVERIFY(udpSocket.bind(QHostAddress("127.0.0.1"), 0)); + QVERIFY(udpSocket.state() == QAbstractSocket::BoundState); + quint16 port = udpSocket.localPort(); + QVERIFY(port != 0); + + // Initialize device #2 + PLATFORMSOCKETENGINE udpSocket2; + QVERIFY(udpSocket2.initialize(QAbstractSocket::UdpSocket)); + + // Connect device #2 to #1 + QVERIFY(udpSocket2.connectToHost(QHostAddress("127.0.0.1"), port)); + QVERIFY(udpSocket2.state() == QAbstractSocket::ConnectedState); + + // Write a message to #1 + QByteArray message1 = "hei der"; + QVERIFY(udpSocket2.write(message1.data(), + message1.size()) == message1.size()); + + // Read the message from #2 + QVERIFY(udpSocket.waitForRead()); + QVERIFY(udpSocket.hasPendingDatagrams()); + qint64 available = udpSocket.pendingDatagramSize(); + QVERIFY(available > 0); + QByteArray answer; + answer.resize(available); + QHostAddress senderAddress; + quint16 senderPort = 0; + QVERIFY(udpSocket.readDatagram(answer.data(), answer.size(), + &senderAddress, + &senderPort) == message1.size()); + QVERIFY(senderAddress == QHostAddress("127.0.0.1")); + QVERIFY(senderPort != 0); +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::udpIPv6LoopbackTest() +{ + PLATFORMSOCKETENGINE udpSocket; + + // Initialize device #1 + bool init = udpSocket.initialize(QAbstractSocket::UdpSocket, QAbstractSocket::IPv6Protocol); + + if (!init) { + QVERIFY(udpSocket.error() == QAbstractSocket::UnsupportedSocketOperationError); + } else { + QVERIFY(udpSocket.protocol() == QAbstractSocket::IPv6Protocol); + + // Bind #1 to localhost + QVERIFY(udpSocket.bind(QHostAddress("::1"), 0)); + QVERIFY(udpSocket.state() == QAbstractSocket::BoundState); + quint16 port = udpSocket.localPort(); + QVERIFY(port != 0); + + // Initialize device #2 + PLATFORMSOCKETENGINE udpSocket2; + QVERIFY(udpSocket2.initialize(QAbstractSocket::UdpSocket, QAbstractSocket::IPv6Protocol)); + + // Connect device #2 to #1 + QVERIFY(udpSocket2.connectToHost(QHostAddress("::1"), port)); + QVERIFY(udpSocket2.state() == QAbstractSocket::ConnectedState); + + // Write a message to #1 + QByteArray message1 = "hei der"; + QVERIFY(udpSocket2.write(message1.data(), + message1.size()) == message1.size()); + + // Read the message from #2 + QVERIFY(udpSocket.waitForRead()); + QVERIFY(udpSocket.hasPendingDatagrams()); + qint64 available = udpSocket.pendingDatagramSize(); + QVERIFY(available > 0); + QByteArray answer; + answer.resize(available); + QHostAddress senderAddress; + quint16 senderPort = 0; + QVERIFY(udpSocket.readDatagram(answer.data(), answer.size(), + &senderAddress, + &senderPort) == message1.size()); + QVERIFY(senderAddress == QHostAddress("::1")); + QVERIFY(senderPort != 0); + } +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::broadcastTest() +{ +#ifdef Q_OS_SYMBIAN + //broadcast isn't supported on loopback connections, but is on WLAN +#ifndef QT_NO_BEARERMANAGEMENT + QScopedPointer netConfMan(new QNetworkConfigurationManager()); + QNetworkConfiguration networkConfiguration(netConfMan->defaultConfiguration()); + QScopedPointer networkSession(new QNetworkSession(networkConfiguration)); + if (!networkSession->isOpen()) { + networkSession->open(); + bool ok = networkSession->waitForOpened(30000); + qDebug() << networkSession->isOpen() << networkSession->error() << networkSession->errorString(); + QVERIFY(ok); + } +#endif +#endif +#ifdef Q_OS_AIX + QSKIP("Broadcast does not work on darko", SkipAll); +#endif + PLATFORMSOCKETENGINE broadcastSocket; + + // Initialize a regular Udp socket + QVERIFY(broadcastSocket.initialize(QAbstractSocket::UdpSocket)); + + // Bind to any port on all interfaces + QVERIFY(broadcastSocket.bind(QHostAddress::Any, 0)); + QVERIFY(broadcastSocket.state() == QAbstractSocket::BoundState); + quint16 port = broadcastSocket.localPort(); + QVERIFY(port > 0); + + // Broadcast an inappropriate troll message + QByteArray trollMessage + = "MOOT wtf is a MOOT? talk english not your sutpiD ENGLISH."; + qint64 written = broadcastSocket.writeDatagram(trollMessage.data(), + trollMessage.size(), + QHostAddress::Broadcast, + port); + +#ifdef Q_OS_SYMBIAN + //On symbian, broadcasts return 0 bytes written if none of the interfaces support it. + //Notably the loopback interfaces do not. (though they do support multicast!?) + if (written == 0) + QEXPECT_FAIL("", "No active interface supports broadcast", Abort); +#endif + QCOMPARE((int)written, trollMessage.size()); + + // Wait until we receive it ourselves +#if defined(Q_OS_FREEBSD) + QEXPECT_FAIL("", "Broadcasting to 255.255.255.255 does not work on FreeBSD", Abort); +#endif + QVERIFY(broadcastSocket.waitForRead()); + QVERIFY(broadcastSocket.hasPendingDatagrams()); + + qlonglong available = broadcastSocket.pendingDatagramSize(); + QByteArray response; + response.resize(available); + QVERIFY(broadcastSocket.readDatagram(response.data(), response.size()) + == response.size()); + QCOMPARE(response, trollMessage); + +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::serverTest() +{ + PLATFORMSOCKETENGINE server; + + // Initialize a Tcp socket + QVERIFY(server.initialize(QAbstractSocket::TcpSocket)); + + // Bind to any port on all interfaces + QVERIFY(server.bind(QHostAddress("0.0.0.0"), 0)); + QVERIFY(server.state() == QAbstractSocket::BoundState); + quint16 port = server.localPort(); + + // Listen for incoming connections + QVERIFY(server.listen()); + QVERIFY(server.state() == QAbstractSocket::ListeningState); + + // Initialize a Tcp socket + PLATFORMSOCKETENGINE client; + QVERIFY(client.initialize(QAbstractSocket::TcpSocket)); + if (!client.connectToHost(QHostAddress("127.0.0.1"), port)) { + QVERIFY(client.state() == QAbstractSocket::ConnectingState); + QVERIFY(client.waitForWrite()); + QVERIFY(client.state() == QAbstractSocket::ConnectedState); + } + + // The server accepts the connection + int socketDescriptor = server.accept(); + QVERIFY(socketDescriptor > 0); + + // A socket device is initialized on the server side, passing the + // socket descriptor from accept(). It's pre-connected. + PLATFORMSOCKETENGINE serverSocket; + QVERIFY(serverSocket.initialize(socketDescriptor)); + QVERIFY(serverSocket.state() == QAbstractSocket::ConnectedState); + + // The server socket sends a greeting to the clietn + QByteArray greeting = "Greetings!"; + QVERIFY(serverSocket.write(greeting.data(), + greeting.size()) == greeting.size()); + + // The client waits for the greeting to arrive + QVERIFY(client.waitForRead()); + qint64 available = client.bytesAvailable(); + QVERIFY(available > 0); + + // The client reads the greeting and checks that it's correct + QByteArray response; + response.resize(available); + QVERIFY(client.read(response.data(), + response.size()) == response.size()); + QCOMPARE(response, greeting); +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::udpLoopbackPerformance() +{ +#ifdef SYMBIAN_WINSOCK_CONNECTIVITY + QSKIP("Not working on Emulator without WinPCAP", SkipAll); +#endif + PLATFORMSOCKETENGINE udpSocket; + + // Initialize device #1 + QVERIFY(udpSocket.initialize(QAbstractSocket::UdpSocket)); + QVERIFY(udpSocket.isValid()); + QVERIFY(udpSocket.socketDescriptor() != -1); + QVERIFY(udpSocket.protocol() == QAbstractSocket::IPv4Protocol); + QVERIFY(udpSocket.socketType() == QAbstractSocket::UdpSocket); + QVERIFY(udpSocket.state() == QAbstractSocket::UnconnectedState); + + // Bind #1 to localhost + QVERIFY(udpSocket.bind(QHostAddress("127.0.0.1"), 0)); + QVERIFY(udpSocket.state() == QAbstractSocket::BoundState); + quint16 port = udpSocket.localPort(); + QVERIFY(port != 0); + + // Initialize device #2 + PLATFORMSOCKETENGINE udpSocket2; + QVERIFY(udpSocket2.initialize(QAbstractSocket::UdpSocket)); + + // Connect device #2 to #1 + QVERIFY(udpSocket2.connectToHost(QHostAddress("127.0.0.1"), port)); + QVERIFY(udpSocket2.state() == QAbstractSocket::ConnectedState); + + const int messageSize = 8192; + QByteArray message1(messageSize, '@'); + QByteArray answer(messageSize, '@'); + + QHostAddress localhost = QHostAddress::LocalHost; + + qlonglong readBytes = 0; + QTime timer; + timer.start(); + while (timer.elapsed() < 5000) { + udpSocket2.write(message1.data(), message1.size()); + udpSocket.waitForRead(); + while (udpSocket.hasPendingDatagrams()) { + readBytes += (qlonglong) udpSocket.readDatagram(answer.data(), + answer.size()); + } + } + + qDebug("\t\t%.1fMB/%.1fs: %.1fMB/s", + readBytes / (1024.0 * 1024.0), + timer.elapsed() / 1024.0, + (readBytes / (timer.elapsed() / 1000.0)) / (1024 * 1024)); +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::tcpLoopbackPerformance() +{ + PLATFORMSOCKETENGINE server; + + // Initialize a Tcp socket + QVERIFY(server.initialize(QAbstractSocket::TcpSocket)); + + // Bind to any port on all interfaces + QVERIFY(server.bind(QHostAddress("0.0.0.0"), 0)); + QVERIFY(server.state() == QAbstractSocket::BoundState); + quint16 port = server.localPort(); + + // Listen for incoming connections + QVERIFY(server.listen()); + QVERIFY(server.state() == QAbstractSocket::ListeningState); + + // Initialize a Tcp socket + PLATFORMSOCKETENGINE client; + QVERIFY(client.initialize(QAbstractSocket::TcpSocket)); + + // Connect to our server + if (!client.connectToHost(QHostAddress("127.0.0.1"), port)) { + QVERIFY(client.state() == QAbstractSocket::ConnectingState); + QVERIFY(client.waitForWrite()); + QVERIFY(client.state() == QAbstractSocket::ConnectedState); + } + + // The server accepts the connection + int socketDescriptor = server.accept(); + QVERIFY(socketDescriptor > 0); + + // A socket device is initialized on the server side, passing the + // socket descriptor from accept(). It's pre-connected. + PLATFORMSOCKETENGINE serverSocket; + QVERIFY(serverSocket.initialize(socketDescriptor)); + QVERIFY(serverSocket.state() == QAbstractSocket::ConnectedState); + +#if defined (Q_OS_SYMBIAN) && defined (__WINS__) + const int messageSize = 1024 * 16; +#else + const int messageSize = 1024 * 256; +#endif + QByteArray message1(messageSize, '@'); + QByteArray answer(messageSize, '@'); + + QTime timer; + timer.start(); + qlonglong readBytes = 0; + while (timer.elapsed() < 5000) { + qlonglong written = serverSocket.write(message1.data(), message1.size()); + while (written > 0) { + client.waitForRead(); + if (client.bytesAvailable() > 0) { + qlonglong readNow = client.read(answer.data(), answer.size()); + written -= readNow; + readBytes += readNow; + } + } + } + + qDebug("\t\t%.1fMB/%.1fs: %.1fMB/s", + readBytes / (1024.0 * 1024.0), + timer.elapsed() / 1024.0, + (readBytes / (timer.elapsed() / 1000.0)) / (1024 * 1024)); +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::readWriteBufferSize() +{ + PLATFORMSOCKETENGINE device; + + QVERIFY(device.initialize(QAbstractSocket::TcpSocket)); + + qint64 bufferSize = device.receiveBufferSize(); + QVERIFY(bufferSize != -1); + device.setReceiveBufferSize(bufferSize + 1); +#if defined(Q_OS_WINCE) + QEXPECT_FAIL(0, "Not supported by default on WinCE", Continue); +#endif + QVERIFY(device.receiveBufferSize() > bufferSize); + + bufferSize = device.sendBufferSize(); + QVERIFY(bufferSize != -1); + device.setSendBufferSize(bufferSize + 1); + QVERIFY(device.sendBufferSize() > bufferSize); + +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::tooManySockets() +{ +#if defined Q_OS_WIN + QSKIP("Certain windows machines suffocate and spend too much time in this test.", SkipAll); +#endif + QList sockets; + PLATFORMSOCKETENGINE *socketLayer = 0; + for (;;) { + socketLayer = new PLATFORMSOCKETENGINE; + sockets.append(socketLayer); + + if (!socketLayer->initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)) + break; + } + + QCOMPARE(socketLayer->error(), QAbstractSocket::SocketResourceError); + + qDeleteAll(sockets); +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::bind() +{ +#if !defined Q_OS_WIN && !defined Q_OS_SYMBIAN + PLATFORMSOCKETENGINE binder; + QVERIFY(binder.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + QVERIFY(!binder.bind(QHostAddress::Any, 82)); + QVERIFY(binder.error() == QAbstractSocket::SocketAccessError); +#endif + + PLATFORMSOCKETENGINE binder2; + QVERIFY(binder2.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + QVERIFY(binder2.bind(QHostAddress::Any, 31180)); + + PLATFORMSOCKETENGINE binder3; + QVERIFY(binder3.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + QVERIFY(!binder3.bind(QHostAddress::Any, 31180)); + +#ifdef SYMBIAN_WINSOCK_CONNECTIVITY + qDebug("On Symbian Emulator (WinSock) we get EADDRNOTAVAIL instead of EADDRINUSE"); + QVERIFY(binder3.error() == QAbstractSocket::SocketAddressNotAvailableError); +#else + QVERIFY(binder3.error() == QAbstractSocket::AddressInUseError); +#endif +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::networkError() +{ + PLATFORMSOCKETENGINE client; + + QVERIFY(client.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + + const bool isConnected = client.connectToHost(QtNetworkSettings::serverIP(), 143); + if (!isConnected) { + QVERIFY(client.state() == QAbstractSocket::ConnectingState); + QVERIFY(client.waitForWrite()); + QVERIFY(client.state() == QAbstractSocket::ConnectedState); + } + QVERIFY(client.state() == QAbstractSocket::ConnectedState); + + // An unexpected network error! +#ifdef Q_OS_WIN + // could use shutdown to produce different errors + ::closesocket(client.socketDescriptor()); +#elif defined(Q_OS_SYMBIAN) + RSocket sock; + QVERIFY(QSymbianSocketManager::instance().lookupSocket(client.socketDescriptor(), sock)); + TRequestStatus stat; + sock.Shutdown(RSocket::EImmediate, stat); + User::WaitForRequest(stat); +#else + ::close(client.socketDescriptor()); +#endif + + QVERIFY(client.read(0, 0) == -1); +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::setSocketDescriptor() +{ + PLATFORMSOCKETENGINE socket1; + QVERIFY(socket1.initialize(QAbstractSocket::TcpSocket)); + + PLATFORMSOCKETENGINE socket2; + QVERIFY(socket2.initialize(socket1.socketDescriptor())); +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::invalidSend() +{ + PLATFORMSOCKETENGINE socket; + QVERIFY(socket.initialize(QAbstractSocket::TcpSocket)); + + QTest::ignoreMessage(QtWarningMsg, PLATFORMSOCKETENGINESTRING "::writeDatagram() was" + " called by a socket other than QAbstractSocket::UdpSocket"); + QCOMPARE(socket.writeDatagram("hei", 3, QHostAddress::LocalHost, 143), + (qlonglong) -1); +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::receiveUrgentData() +{ + PLATFORMSOCKETENGINE server; + + QVERIFY(server.initialize(QAbstractSocket::TcpSocket)); + + // Bind to any port on all interfaces + QVERIFY(server.bind(QHostAddress("0.0.0.0"), 0)); + QVERIFY(server.state() == QAbstractSocket::BoundState); + quint16 port = server.localPort(); + + QVERIFY(server.listen()); + QVERIFY(server.state() == QAbstractSocket::ListeningState); + + PLATFORMSOCKETENGINE client; + QVERIFY(client.initialize(QAbstractSocket::TcpSocket)); + + if (!client.connectToHost(QHostAddress("127.0.0.1"), port)) { + QVERIFY(client.state() == QAbstractSocket::ConnectingState); + QVERIFY(client.waitForWrite()); + QVERIFY(client.state() == QAbstractSocket::ConnectedState); + } + + int socketDescriptor = server.accept(); + QVERIFY(socketDescriptor > 0); + + PLATFORMSOCKETENGINE serverSocket; + QVERIFY(serverSocket.initialize(socketDescriptor)); + QVERIFY(serverSocket.state() == QAbstractSocket::ConnectedState); + + char msg; + int available; + QByteArray response; + +#if defined Q_OS_HPUX + QSKIP("Native OOB data test doesn't work on HP-UX.", SkipAll); +#elif defined (Q_OS_WINCE) + QSKIP("Native OOB data test doesn't work on WinCE.", SkipAll); +#endif + + // The server sends an urgent message + msg = 'Q'; +#if defined(Q_OS_SYMBIAN) + RSocket sock; + QVERIFY(QSymbianSocketManager::instance().lookupSocket(socketDescriptor, sock)); + TRequestStatus stat; + TSockXfrLength len; + sock.Send(TPtrC8((TUint8*)&msg,1), KSockWriteUrgent, stat, len); + User::WaitForRequest(stat); + QVERIFY(stat == KErrNone); + QCOMPARE(len(), 1); +#else + QCOMPARE(int(::send(socketDescriptor, &msg, sizeof(msg), MSG_OOB)), 1); +#endif + + // The client receives the urgent message + QVERIFY(client.waitForRead()); + available = client.bytesAvailable(); + QCOMPARE(available, 1); + response.resize(available); + QCOMPARE(client.read(response.data(), response.size()), qint64(1)); + QCOMPARE(response.at(0), msg); + + // The client sends an urgent message + msg = 'T'; + int clientDescriptor = client.socketDescriptor(); +#if defined(Q_OS_SYMBIAN) + QVERIFY(QSymbianSocketManager::instance().lookupSocket(clientDescriptor, sock)); + sock.Send(TPtrC8((TUint8*)&msg,1), KSockWriteUrgent, stat, len); + User::WaitForRequest(stat); + QVERIFY(stat == KErrNone); + QCOMPARE(len(), 1); +#else + QCOMPARE(int(::send(clientDescriptor, &msg, sizeof(msg), MSG_OOB)), 1); +#endif + + // The server receives the urgent message + QVERIFY(serverSocket.waitForRead()); + available = serverSocket.bytesAvailable(); + QCOMPARE(available, 1); + response.resize(available); + QCOMPARE(serverSocket.read(response.data(), response.size()), qint64(1)); + QCOMPARE(response.at(0), msg); + +} + +QTEST_MAIN(tst_PlatformSocketEngine) +#include "tst_platformsocketengine.moc" diff --git a/tests/auto/network/socket/qabstractsocket/.gitignore b/tests/auto/network/socket/qabstractsocket/.gitignore new file mode 100644 index 0000000000..b962a1a54b --- /dev/null +++ b/tests/auto/network/socket/qabstractsocket/.gitignore @@ -0,0 +1 @@ +tst_qabstractsocket diff --git a/tests/auto/network/socket/qabstractsocket/qabstractsocket.pro b/tests/auto/network/socket/qabstractsocket/qabstractsocket.pro new file mode 100644 index 0000000000..814a7d2600 --- /dev/null +++ b/tests/auto/network/socket/qabstractsocket/qabstractsocket.pro @@ -0,0 +1,11 @@ +############################################################ +# Project file for autotest for file qabstractsocket.h +############################################################ + +load(qttest_p4) +QT = core network + +SOURCES += tst_qabstractsocket.cpp + +symbian: TARGET.CAPABILITY = NetworkServices + diff --git a/tests/auto/network/socket/qabstractsocket/tst_qabstractsocket.cpp b/tests/auto/network/socket/qabstractsocket/tst_qabstractsocket.cpp new file mode 100644 index 0000000000..309215d5ea --- /dev/null +++ b/tests/auto/network/socket/qabstractsocket/tst_qabstractsocket.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include + +#include +#include +#include + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QAbstractSocket : public QObject +{ +Q_OBJECT + +public: + tst_QAbstractSocket(); + virtual ~tst_QAbstractSocket(); + +private slots: + void getSetCheck(); +}; + +tst_QAbstractSocket::tst_QAbstractSocket() +{ +} + +tst_QAbstractSocket::~tst_QAbstractSocket() +{ +} + +class MyAbstractSocket : public QAbstractSocket +{ +public: + MyAbstractSocket() : QAbstractSocket(QAbstractSocket::TcpSocket, 0) {} + void setLocalPort(quint16 port) { QAbstractSocket::setLocalPort(port); } + void setPeerPort(quint16 port) { QAbstractSocket::setPeerPort(port); } +}; + +// Testing get/set functions +void tst_QAbstractSocket::getSetCheck() +{ + MyAbstractSocket obj1; + // qint64 QAbstractSocket::readBufferSize() + // void QAbstractSocket::setReadBufferSize(qint64) + obj1.setReadBufferSize(qint64(0)); + QCOMPARE(qint64(0), obj1.readBufferSize()); + obj1.setReadBufferSize((Q_INT64_C(-9223372036854775807) - 1)); + QCOMPARE((Q_INT64_C(-9223372036854775807) - 1), obj1.readBufferSize()); + obj1.setReadBufferSize(Q_INT64_C(9223372036854775807)); + QCOMPARE(Q_INT64_C(9223372036854775807), obj1.readBufferSize()); + + // quint16 QAbstractSocket::localPort() + // void QAbstractSocket::setLocalPort(quint16) + obj1.setLocalPort(quint16(0)); + QCOMPARE(quint16(0), obj1.localPort()); + obj1.setLocalPort(quint16(0xffff)); + QCOMPARE(quint16(0xffff), obj1.localPort()); + + // quint16 QAbstractSocket::peerPort() + // void QAbstractSocket::setPeerPort(quint16) + obj1.setPeerPort(quint16(0)); + QCOMPARE(quint16(0), obj1.peerPort()); + obj1.setPeerPort(quint16(0xffff)); + QCOMPARE(quint16(0xffff), obj1.peerPort()); +} + +QTEST_MAIN(tst_QAbstractSocket) +#include "tst_qabstractsocket.moc" diff --git a/tests/auto/network/socket/qhttpsocketengine/.gitignore b/tests/auto/network/socket/qhttpsocketengine/.gitignore new file mode 100644 index 0000000000..c3eb6526ba --- /dev/null +++ b/tests/auto/network/socket/qhttpsocketengine/.gitignore @@ -0,0 +1 @@ +tst_qhttpsocketengine diff --git a/tests/auto/network/socket/qhttpsocketengine/qhttpsocketengine.pro b/tests/auto/network/socket/qhttpsocketengine/qhttpsocketengine.pro new file mode 100644 index 0000000000..f26abbe8d9 --- /dev/null +++ b/tests/auto/network/socket/qhttpsocketengine/qhttpsocketengine.pro @@ -0,0 +1,13 @@ +load(qttest_p4) +SOURCES += tst_qhttpsocketengine.cpp + + +include(../platformsocketengine/platformsocketengine.pri) + +MOC_DIR=tmp + +QT = core-private network-private + +symbian: TARGET.CAPABILITY = NetworkServices + + diff --git a/tests/auto/network/socket/qhttpsocketengine/tst_qhttpsocketengine.cpp b/tests/auto/network/socket/qhttpsocketengine/tst_qhttpsocketengine.cpp new file mode 100644 index 0000000000..5a90abea0e --- /dev/null +++ b/tests/auto/network/socket/qhttpsocketengine/tst_qhttpsocketengine.cpp @@ -0,0 +1,748 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../network-settings.h" + +class tst_QHttpSocketEngine : public QObject +{ + Q_OBJECT + +public: + tst_QHttpSocketEngine(); + virtual ~tst_QHttpSocketEngine(); + + +public slots: + void init(); + void cleanup(); +private slots: + void construction(); + void errorTest_data(); + void errorTest(); + void simpleConnectToIMAP(); + void simpleErrorsAndStates(); + + void tcpSocketBlockingTest(); + void tcpSocketNonBlockingTest(); + void downloadBigFile(); + // void tcpLoopbackPerformance(); + void passwordAuth(); + +protected slots: + void tcpSocketNonBlocking_hostFound(); + void tcpSocketNonBlocking_connected(); + void tcpSocketNonBlocking_closed(); + void tcpSocketNonBlocking_readyRead(); + void tcpSocketNonBlocking_bytesWritten(qint64); + void exitLoopSlot(); + void downloadBigFileSlot(); + +private: + QTcpSocket *tcpSocketNonBlocking_socket; + QStringList tcpSocketNonBlocking_data; + qint64 tcpSocketNonBlocking_totalWritten; + QTcpSocket *tmpSocket; + qint64 bytesAvailable; +}; + +class MiniHttpServer: public QTcpServer +{ + Q_OBJECT + QTcpSocket *client; + QList dataToTransmit; + +public: + QByteArray receivedData; + + MiniHttpServer(const QList &data) : client(0), dataToTransmit(data) + { + listen(); + connect(this, SIGNAL(newConnection()), this, SLOT(doAccept())); + } + +public slots: + void doAccept() + { + client = nextPendingConnection(); + connect(client, SIGNAL(readyRead()), this, SLOT(sendData())); + } + + void sendData() + { + receivedData += client->readAll(); + int idx = client->property("dataTransmitionIdx").toInt(); + if (receivedData.contains("\r\n\r\n") || + receivedData.contains("\n\n")) { + if (idx < dataToTransmit.length()) + client->write(dataToTransmit.at(idx++)); + if (idx == dataToTransmit.length()) { + client->disconnectFromHost(); + disconnect(client, 0, this, 0); + client = 0; + } else { + client->setProperty("dataTransmitionIdx", idx); + } + } + } +}; + +tst_QHttpSocketEngine::tst_QHttpSocketEngine() +{ + Q_SET_DEFAULT_IAP +} + +tst_QHttpSocketEngine::~tst_QHttpSocketEngine() +{ +} + + +void tst_QHttpSocketEngine::init() +{ + tmpSocket = 0; + bytesAvailable = 0; +} + +void tst_QHttpSocketEngine::cleanup() +{ +} + +//--------------------------------------------------------------------------- +void tst_QHttpSocketEngine::construction() +{ + QHttpSocketEngine socketDevice; + + QVERIFY(!socketDevice.isValid()); + + // Initialize device + QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + QVERIFY(socketDevice.isValid()); + QVERIFY(socketDevice.protocol() == QAbstractSocket::IPv4Protocol); + QVERIFY(socketDevice.socketType() == QAbstractSocket::TcpSocket); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); + // QVERIFY(socketDevice.socketDescriptor() != -1); + QVERIFY(socketDevice.localAddress() == QHostAddress()); + QVERIFY(socketDevice.localPort() == 0); + QVERIFY(socketDevice.peerAddress() == QHostAddress()); + QVERIFY(socketDevice.peerPort() == 0); + QVERIFY(socketDevice.error() == QAbstractSocket::UnknownSocketError); + + //QTest::ignoreMessage(QtWarningMsg, "QSocketLayer::bytesAvailable() was called in QAbstractSocket::UnconnectedState"); + QVERIFY(socketDevice.bytesAvailable() == 0); + + //QTest::ignoreMessage(QtWarningMsg, "QSocketLayer::hasPendingDatagrams() was called in QAbstractSocket::UnconnectedState"); + QVERIFY(!socketDevice.hasPendingDatagrams()); +} + +//--------------------------------------------------------------------------- +void tst_QHttpSocketEngine::errorTest_data() +{ + QTest::addColumn("hostname"); + QTest::addColumn("port"); + QTest::addColumn("username"); + QTest::addColumn("response"); + QTest::addColumn("expectedError"); + + QQueue responses; + QTest::newRow("proxy-host-not-found") << "this-host-does-not-exist." << 1080 << QString() + << QString() + << int(QAbstractSocket::ProxyNotFoundError); + QTest::newRow("proxy-connection-refused") << QtNetworkSettings::serverName() << 2 << QString() + << QString() + << int(QAbstractSocket::ProxyConnectionRefusedError); + + QTest::newRow("garbage1") << QString() << 0 << QString() + << "This is not HTTP\r\n\r\n" + << int(QAbstractSocket::ProxyProtocolError); + + QTest::newRow("garbage2") << QString() << 0 << QString() + << "This is not HTTP" + << int(QAbstractSocket::ProxyConnectionClosedError); + + QTest::newRow("garbage3") << QString() << 0 << QString() + << "" + << int(QAbstractSocket::ProxyConnectionClosedError); + + QTest::newRow("forbidden") << QString() << 0 << QString() + << "HTTP/1.0 403 Forbidden\r\n\r\n" + << int(QAbstractSocket::SocketAccessError); + + QTest::newRow("method-not-allowed") << QString() << 0 << QString() + << "HTTP/1.0 405 Method Not Allowed\r\n\r\n" + << int(QAbstractSocket::SocketAccessError); + + QTest::newRow("proxy-authentication-too-short") + << QString() << 0 << "foo" + << "HTTP/1.0 407 Proxy Authentication Required\r\n\r\n" + << int(QAbstractSocket::ProxyProtocolError); + + QTest::newRow("proxy-authentication-invalid-method") + << QString() << 0 << "foo" + << "HTTP/1.0 407 Proxy Authentication Required\r\n" + "Proxy-Authenticate: Frobnicator\r\n\r\n" + << int(QAbstractSocket::ProxyProtocolError); + + QTest::newRow("proxy-authentication-required") + << QString() << 0 << "foo" + << "HTTP/1.0 407 Proxy Authentication Required\r\n" + "Proxy-Connection: close\r\n" + "Proxy-Authenticate: Basic, realm=wonderland\r\n\r\n" + << int(QAbstractSocket::ProxyAuthenticationRequiredError); + + QTest::newRow("proxy-authentication-required2") + << QString() << 0 << "foo" + << "HTTP/1.0 407 Proxy Authentication Required\r\n" + "Proxy-Connection: keep-alive\r\n" + "Proxy-Authenticate: Basic, realm=wonderland\r\n\r\n" + "\1" + "HTTP/1.0 407 Proxy Authentication Required\r\n" + "Proxy-Authenticate: Basic, realm=wonderland\r\n\r\n" + << int(QAbstractSocket::ProxyAuthenticationRequiredError); + + QTest::newRow("proxy-authentication-required-noclose") + << QString() << 0 << "foo" + << "HTTP/1.0 407 Proxy Authentication Required\r\n" + "Proxy-Authenticate: Basic\r\n" + "\r\n" + << int(QAbstractSocket::ProxyAuthenticationRequiredError); + + QTest::newRow("connection-refused") << QString() << 0 << QString() + << "HTTP/1.0 503 Service Unavailable\r\n\r\n" + << int(QAbstractSocket::ConnectionRefusedError); + + QTest::newRow("host-not-found") << QString() << 0 << QString() + << "HTTP/1.0 404 Not Found\r\n\r\n" + << int(QAbstractSocket::HostNotFoundError); + + QTest::newRow("weird-http-reply") << QString() << 0 << QString() + << "HTTP/1.0 206 Partial Content\r\n\r\n" + << int(QAbstractSocket::ProxyProtocolError); +} + +void tst_QHttpSocketEngine::errorTest() +{ + QFETCH(QString, hostname); + QFETCH(int, port); + QFETCH(QString, username); + QFETCH(QString, response); + QFETCH(int, expectedError); + + MiniHttpServer server(response.toLatin1().split('\1')); + + if (hostname.isEmpty()) { + hostname = "127.0.0.1"; + port = server.serverPort(); + } + QTcpSocket socket; + socket.setProxy(QNetworkProxy(QNetworkProxy::HttpProxy, hostname, port, username, username)); + socket.connectToHost("0.1.2.3", 12345); + + connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), + &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(5); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(int(socket.error()), expectedError); +} + +//--------------------------------------------------------------------------- +void tst_QHttpSocketEngine::simpleConnectToIMAP() +{ + QHttpSocketEngine socketDevice; + + // Initialize device + QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); + + socketDevice.setProxy(QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3128)); + + QVERIFY(!socketDevice.connectToHost(QtNetworkSettings::serverIP(), 143)); + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectingState); + QVERIFY(socketDevice.waitForWrite()); + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectedState); + QVERIFY(socketDevice.peerAddress() == QtNetworkSettings::serverIP()); + QVERIFY(!socketDevice.localAddress().isNull()); + QVERIFY(socketDevice.localPort() > 0); + + // Wait for the greeting + QVERIFY(socketDevice.waitForRead()); + + // Read the greeting + qint64 available = socketDevice.bytesAvailable(); + QVERIFY(available > 0); + QByteArray array; + array.resize(available); + QVERIFY(socketDevice.read(array.data(), array.size()) == available); + + // Check that the greeting is what we expect it to be + QVERIFY2(QtNetworkSettings::compareReplyIMAP(array), array.constData()); + + + // Write a logout message + QByteArray array2 = "XXXX LOGOUT\r\n"; + QVERIFY(socketDevice.write(array2.data(), + array2.size()) == array2.size()); + + // Wait for the response + QVERIFY(socketDevice.waitForRead()); + + available = socketDevice.bytesAvailable(); + QVERIFY(available > 0); + array.resize(available); + QVERIFY(socketDevice.read(array.data(), array.size()) == available); + + // Check that the greeting is what we expect it to be + QCOMPARE(array.constData(), "* BYE LOGOUT received\r\nXXXX OK Completed\r\n"); + + // Wait for the response + QVERIFY(socketDevice.waitForRead()); + char c; + QCOMPARE(socketDevice.read(&c, sizeof(c)), (qint64) -1); + QVERIFY(socketDevice.error() == QAbstractSocket::RemoteHostClosedError); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); +} + +//--------------------------------------------------------------------------- +void tst_QHttpSocketEngine::simpleErrorsAndStates() +{ + { + QHttpSocketEngine socketDevice; + + // Initialize device + QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + + socketDevice.setProxy(QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3128)); + + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); + QVERIFY(!socketDevice.connectToHost(QHostAddress(QtNetworkSettings::serverName()), 8088)); + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectingState); + if (socketDevice.waitForWrite(15000)) { + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectedState || + socketDevice.state() == QAbstractSocket::UnconnectedState); + } else { + QVERIFY(socketDevice.error() == QAbstractSocket::SocketTimeoutError); + } + } + +} + +/* +//--------------------------------------------------------------------------- +void tst_QHttpSocketEngine::tcpLoopbackPerformance() +{ + QTcpServer server; + + // Bind to any port on all interfaces + QVERIFY(server.bind(QHostAddress("0.0.0.0"), 0)); + QVERIFY(server.state() == QAbstractSocket::BoundState); + quint16 port = server.localPort(); + + // Listen for incoming connections + QVERIFY(server.listen()); + QVERIFY(server.state() == QAbstractSocket::ListeningState); + + // Initialize a Tcp socket + QHttpSocketEngine client; + QVERIFY(client.initialize(QAbstractSocket::TcpSocket)); + + client.setProxy(QHostAddress("80.232.37.158"), 1081); + + // Connect to our server + if (!client.connectToHost(QHostAddress("127.0.0.1"), port)) { + QVERIFY(client.waitForWrite()); + QVERIFY(client.connectToHost(QHostAddress("127.0.0.1"), port)); + } + + // The server accepts the connectio + int socketDescriptor = server.accept(); + QVERIFY(socketDescriptor > 0); + + // A socket device is initialized on the server side, passing the + // socket descriptor from accept(). It's pre-connected. + QSocketLayer serverSocket; + QVERIFY(serverSocket.initialize(socketDescriptor)); + QVERIFY(serverSocket.state() == QAbstractSocket::ConnectedState); + + const int messageSize = 1024 * 256; + QByteArray message1(messageSize, '@'); + QByteArray answer(messageSize, '@'); + + QTime timer; + timer.start(); + qlonglong readBytes = 0; + while (timer.elapsed() < 5000) { + qlonglong written = serverSocket.write(message1.data(), message1.size()); + while (written > 0) { + client.waitForRead(); + if (client.bytesAvailable() > 0) { + qlonglong readNow = client.read(answer.data(), answer.size()); + written -= readNow; + readBytes += readNow; + } + } + } + + qDebug("\t\t%.1fMB/%.1fs: %.1fMB/s", + readBytes / (1024.0 * 1024.0), + timer.elapsed() / 1024.0, + (readBytes / (timer.elapsed() / 1000.0)) / (1024 * 1024)); +} +*/ + + + +void tst_QHttpSocketEngine::tcpSocketBlockingTest() +{ + QHttpSocketEngineHandler http; + + QTcpSocket socket; + + // Connect + socket.connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket.waitForConnected()); + QCOMPARE(socket.state(), QTcpSocket::ConnectedState); + + // Read greeting + QVERIFY(socket.waitForReadyRead(5000)); + QString s = socket.readLine(); + QVERIFY2(QtNetworkSettings::compareReplyIMAP(s.toLatin1()), qPrintable(s)); + + // Write NOOP + QCOMPARE((int) socket.write("1 NOOP\r\n", 8), 8); + + if (!socket.canReadLine()) + QVERIFY(socket.waitForReadyRead(5000)); + + // Read response + s = socket.readLine(); + QCOMPARE(s.toLatin1().constData(), "1 OK Completed\r\n"); + + // Write LOGOUT + QCOMPARE((int) socket.write("2 LOGOUT\r\n", 10), 10); + + if (!socket.canReadLine()) + QVERIFY(socket.waitForReadyRead(5000)); + + // Read two lines of respose + s = socket.readLine(); + QCOMPARE(s.toLatin1().constData(), "* BYE LOGOUT received\r\n"); + + if (!socket.canReadLine()) + QVERIFY(socket.waitForReadyRead(5000)); + + s = socket.readLine(); + QCOMPARE(s.toLatin1().constData(), "2 OK Completed\r\n"); + + // Close the socket + socket.close(); + + // Check that it's closed + QCOMPARE(socket.state(), QTcpSocket::UnconnectedState); +} + +//---------------------------------------------------------------------------------- + +void tst_QHttpSocketEngine::tcpSocketNonBlockingTest() +{ + QHttpSocketEngineHandler http; + + QTcpSocket socket; + connect(&socket, SIGNAL(hostFound()), SLOT(tcpSocketNonBlocking_hostFound())); + connect(&socket, SIGNAL(connected()), SLOT(tcpSocketNonBlocking_connected())); + connect(&socket, SIGNAL(disconnected()), SLOT(tcpSocketNonBlocking_closed())); + connect(&socket, SIGNAL(bytesWritten(qint64)), SLOT(tcpSocketNonBlocking_bytesWritten(qint64))); + connect(&socket, SIGNAL(readyRead()), SLOT(tcpSocketNonBlocking_readyRead())); + tcpSocketNonBlocking_socket = &socket; + + // Connect + socket.connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket.state() == QTcpSocket::HostLookupState || + socket.state() == QTcpSocket::ConnectingState); + + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) { + QFAIL("Timed out"); + } + + if (socket.state() == QTcpSocket::ConnectingState) { + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) { + QFAIL("Timed out"); + } + } + + QCOMPARE(socket.state(), QTcpSocket::ConnectedState); + + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) { + QFAIL("Timed out"); + } + + // Read greeting + QVERIFY(!tcpSocketNonBlocking_data.isEmpty()); + QByteArray data = tcpSocketNonBlocking_data.at(0).toLatin1(); + QVERIFY2(QtNetworkSettings::compareReplyIMAP(data), data.constData()); + + + tcpSocketNonBlocking_data.clear(); + + tcpSocketNonBlocking_totalWritten = 0; + + // Write NOOP + QCOMPARE((int) socket.write("1 NOOP\r\n", 8), 8); + + + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) { + QFAIL("Timed out"); + } + + QVERIFY(tcpSocketNonBlocking_totalWritten == 8); + + + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) { + QFAIL("Timed out"); + } + + + // Read response + QVERIFY(!tcpSocketNonBlocking_data.isEmpty()); + QCOMPARE(tcpSocketNonBlocking_data.at(0).toLatin1().constData(), "1 OK Completed\r\n"); + tcpSocketNonBlocking_data.clear(); + + + tcpSocketNonBlocking_totalWritten = 0; + + // Write LOGOUT + QCOMPARE((int) socket.write("2 LOGOUT\r\n", 10), 10); + + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) { + QFAIL("Timed out"); + } + + QVERIFY(tcpSocketNonBlocking_totalWritten == 10); + + // Wait for greeting + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) { + QFAIL("Timed out"); + } + + // Read two lines of respose + QCOMPARE(tcpSocketNonBlocking_data.at(0).toLatin1().constData(), "* BYE LOGOUT received\r\n"); + QCOMPARE(tcpSocketNonBlocking_data.at(1).toLatin1().constData(), "2 OK Completed\r\n"); + tcpSocketNonBlocking_data.clear(); + + // Close the socket + socket.close(); + + // Check that it's closed + QCOMPARE(socket.state(), QTcpSocket::UnconnectedState); +} + +void tst_QHttpSocketEngine::tcpSocketNonBlocking_hostFound() +{ + QTestEventLoop::instance().exitLoop(); +} + +void tst_QHttpSocketEngine::tcpSocketNonBlocking_connected() +{ + QTestEventLoop::instance().exitLoop(); +} + +void tst_QHttpSocketEngine::tcpSocketNonBlocking_readyRead() +{ + while (tcpSocketNonBlocking_socket->canReadLine()) + tcpSocketNonBlocking_data.append(tcpSocketNonBlocking_socket->readLine()); + + QTestEventLoop::instance().exitLoop(); +} + +void tst_QHttpSocketEngine::tcpSocketNonBlocking_bytesWritten(qint64 written) +{ + tcpSocketNonBlocking_totalWritten += written; + QTestEventLoop::instance().exitLoop(); +} + +void tst_QHttpSocketEngine::tcpSocketNonBlocking_closed() +{ +} + +//---------------------------------------------------------------------------------- + +void tst_QHttpSocketEngine::downloadBigFile() +{ + QHttpSocketEngineHandler http; + + if (tmpSocket) + delete tmpSocket; + tmpSocket = new QTcpSocket; + + connect(tmpSocket, SIGNAL(connected()), SLOT(exitLoopSlot())); + connect(tmpSocket, SIGNAL(readyRead()), SLOT(downloadBigFileSlot())); + + tmpSocket->connectToHost(QtNetworkSettings::serverName(), 80); + + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) + QFAIL("Network operation timed out"); + + QByteArray hostName = QtNetworkSettings::serverName().toLatin1(); + QVERIFY(tmpSocket->state() == QAbstractSocket::ConnectedState); + QVERIFY(tmpSocket->write("GET /qtest/mediumfile HTTP/1.0\r\n") > 0); + QVERIFY(tmpSocket->write("Host: ") > 0); + QVERIFY(tmpSocket->write(hostName.data()) > 0); + QVERIFY(tmpSocket->write("\r\n") > 0); + QVERIFY(tmpSocket->write("\r\n") > 0); + + bytesAvailable = 0; + + QTime stopWatch; + stopWatch.start(); + +#if defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN) + QTestEventLoop::instance().enterLoop(240); +#else + QTestEventLoop::instance().enterLoop(60); +#endif + if (QTestEventLoop::instance().timeout()) + QFAIL("Network operation timed out"); + + QVERIFY(bytesAvailable >= 10000000); + + QVERIFY(tmpSocket->state() == QAbstractSocket::ConnectedState); + + qDebug("\t\t%.1fMB/%.1fs: %.1fMB/s", + bytesAvailable / (1024.0 * 1024.0), + stopWatch.elapsed() / 1024.0, + (bytesAvailable / (stopWatch.elapsed() / 1000.0)) / (1024 * 1024)); + + delete tmpSocket; + tmpSocket = 0; +} + +void tst_QHttpSocketEngine::exitLoopSlot() +{ + QTestEventLoop::instance().exitLoop(); +} + + +void tst_QHttpSocketEngine::downloadBigFileSlot() +{ + bytesAvailable += tmpSocket->readAll().size(); + if (bytesAvailable >= 10000000) + QTestEventLoop::instance().exitLoop(); +} + +void tst_QHttpSocketEngine::passwordAuth() +{ + QHttpSocketEngine socketDevice; + + // Initialize device + QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); + + socketDevice.setProxy(QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3128, "qsockstest", "password")); + + QVERIFY(!socketDevice.connectToHost(QtNetworkSettings::serverIP(), 143)); + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectingState); + QVERIFY(socketDevice.waitForWrite()); + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectedState); + QVERIFY(socketDevice.peerAddress() == QtNetworkSettings::serverIP()); + + // Wait for the greeting + QVERIFY(socketDevice.waitForRead()); + + // Read the greeting + qint64 available = socketDevice.bytesAvailable(); + QVERIFY(available > 0); + QByteArray array; + array.resize(available); + QVERIFY(socketDevice.read(array.data(), array.size()) == available); + + // Check that the greeting is what we expect it to be + QVERIFY2(QtNetworkSettings::compareReplyIMAP(array), array.constData()); + + + // Write a logout message + QByteArray array2 = "XXXX LOGOUT\r\n"; + QVERIFY(socketDevice.write(array2.data(), + array2.size()) == array2.size()); + + // Wait for the response + QVERIFY(socketDevice.waitForRead()); + + available = socketDevice.bytesAvailable(); + QVERIFY(available > 0); + array.resize(available); + QVERIFY(socketDevice.read(array.data(), array.size()) == available); + + // Check that the greeting is what we expect it to be + QCOMPARE(array.constData(), "* BYE LOGOUT received\r\nXXXX OK Completed\r\n"); + + // Wait for the response + QVERIFY(socketDevice.waitForRead()); + char c; + QVERIFY(socketDevice.read(&c, sizeof(c)) == -1); + QVERIFY(socketDevice.error() == QAbstractSocket::RemoteHostClosedError); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); +} + +//---------------------------------------------------------------------------------- + +QTEST_MAIN(tst_QHttpSocketEngine) +#include "tst_qhttpsocketengine.moc" diff --git a/tests/auto/network/socket/qlocalsocket/.gitignore b/tests/auto/network/socket/qlocalsocket/.gitignore new file mode 100644 index 0000000000..b45c266ce3 --- /dev/null +++ b/tests/auto/network/socket/qlocalsocket/.gitignore @@ -0,0 +1,2 @@ +tst_qlocalsocket +lackey/lackey.exe diff --git a/tests/auto/network/socket/qlocalsocket/example/client/client.pro b/tests/auto/network/socket/qlocalsocket/example/client/client.pro new file mode 100644 index 0000000000..84f20d6ec0 --- /dev/null +++ b/tests/auto/network/socket/qlocalsocket/example/client/client.pro @@ -0,0 +1,10 @@ +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . +CONFIG += console +QT = core network + +SOURCES += main.cpp + + diff --git a/tests/auto/network/socket/qlocalsocket/example/client/main.cpp b/tests/auto/network/socket/qlocalsocket/example/client/main.cpp new file mode 100644 index 0000000000..acf5cbc388 --- /dev/null +++ b/tests/auto/network/socket/qlocalsocket/example/client/main.cpp @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include +#include + +#include +#include +#include + +#include "qlocalsocket.h" + +#define SOCK_PATH "echo_socket" + +int main(void) +{ + QLocalSocket socket; + socket.connectToServer(SOCK_PATH); + socket.open(QIODevice::ReadWrite); + + printf("Connected.\n"); + char str[100]; + while(printf("> "), fgets(str, 100, stdin), !feof(stdin)) { + if (socket.write(str, strlen(str)) == -1) { + perror("send"); + return EXIT_FAILURE; + } + + int t; + if ((t = socket.read(str, 100)) > 0) { + str[t] = '\0'; + printf("echo> %s", str); + } else { + if (t < 0) + perror("recv"); + else + printf("Server closed connection.\n"); + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} + diff --git a/tests/auto/network/socket/qlocalsocket/example/example.pro b/tests/auto/network/socket/qlocalsocket/example/example.pro new file mode 100644 index 0000000000..8c678cd05a --- /dev/null +++ b/tests/auto/network/socket/qlocalsocket/example/example.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS = client server + diff --git a/tests/auto/network/socket/qlocalsocket/example/server/main.cpp b/tests/auto/network/socket/qlocalsocket/example/server/main.cpp new file mode 100644 index 0000000000..0d3de3a2e1 --- /dev/null +++ b/tests/auto/network/socket/qlocalsocket/example/server/main.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qlocalserver.h" +#include "qlocalsocket.h" + +#include +#include + +class EchoServer : public QLocalServer +{ +public: + void incomingConnection(int socketDescriptor) { + QLocalServer::incomingConnection(socketDescriptor); + QLocalSocket *socket = nextPendingConnection(); + socket->open(QIODevice::ReadWrite); + + qDebug() << "server connection"; + + do { + const int Timeout = 5 * 1000; + while (!socket->canReadLine()) { + if (!socket->waitForReadyRead(Timeout)) { + return; + } + } + char str[100]; + int n = socket->readLine(str, 100); + if (n < 0) { + perror("recv"); + break; + } + if (n == 0) + break; + qDebug() << "Read" << str; + if ("exit" == str) + qApp->quit(); + + if (socket->write(str, 100) < 0) { + perror("send"); + break; + } + } while (true); + } +}; + +#define SOCK_PATH "echo_socket" + +int main(int argc, char *argv[]) +{ + QCoreApplication application(argc, argv); + + EchoServer echoServer; + echoServer.listen(SOCK_PATH); + + return application.exec(); +} + diff --git a/tests/auto/network/socket/qlocalsocket/example/server/server.pro b/tests/auto/network/socket/qlocalsocket/example/server/server.pro new file mode 100644 index 0000000000..bfd14d2bb7 --- /dev/null +++ b/tests/auto/network/socket/qlocalsocket/example/server/server.pro @@ -0,0 +1,13 @@ +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +CONFIG += console + +QT = core network + +# Input +SOURCES += main.cpp + + diff --git a/tests/auto/network/socket/qlocalsocket/lackey/lackey.pro b/tests/auto/network/socket/qlocalsocket/lackey/lackey.pro new file mode 100644 index 0000000000..2573222c8b --- /dev/null +++ b/tests/auto/network/socket/qlocalsocket/lackey/lackey.pro @@ -0,0 +1,16 @@ +#include(../src/src.pri) + +QT = core script network testlib + +DESTDIR = ./ + +win32: CONFIG += console +mac:CONFIG -= app_bundle + +DEFINES += QLOCALSERVER_DEBUG +DEFINES += QLOCALSOCKET_DEBUG + +SOURCES += main.cpp +TARGET = lackey + +symbian:TARGET.CAPABILITY = ALL -TCB diff --git a/tests/auto/network/socket/qlocalsocket/lackey/main.cpp b/tests/auto/network/socket/qlocalsocket/lackey/main.cpp new file mode 100644 index 0000000000..f7b2ff10b3 --- /dev/null +++ b/tests/auto/network/socket/qlocalsocket/lackey/main.cpp @@ -0,0 +1,296 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include + #include +#include + +#include +#include + +class QScriptLocalSocket : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString serverName WRITE connectToServer READ serverName) + +public: + QScriptLocalSocket(QObject *parent = 0) : QObject(parent) + { + lc = new QLocalSocket(this); + } + +public slots: + QString serverName() + { + return lc->serverName(); + } + + void connectToServer(const QString &name) { + lc->connectToServer(name); + } + + void sleep(int x) const + { + QTest::qSleep(x); + } + + bool isConnected() { + return (lc->state() == QLocalSocket::ConnectedState); + } + + void open() { + lc->open(QIODevice::ReadWrite); + } + + bool waitForConnected() { + return lc->waitForConnected(100000); + } + void waitForReadyRead() { + lc->waitForReadyRead(); + } + + void write(const QString &string) { + QTextStream out(lc); + out << string << endl; + } + + bool waitForBytesWritten(int t = 3000) { + return lc->waitForBytesWritten(t); + } + + QString readLine() { + QTextStream in(lc); + return in.readLine(); + } + + QString errorString() { + return lc->errorString(); + } + + void close() { + lc->close(); + } + +public: + QLocalSocket *lc; +}; + +class QScriptLocalServer : public QLocalServer +{ + Q_OBJECT + Q_PROPERTY(int maxPendingConnections WRITE setMaxPendingConnections READ maxPendingConnections) + Q_PROPERTY(QString name WRITE listen READ serverName) + Q_PROPERTY(bool listening READ isListening) + +public: + QScriptLocalServer(QObject *parent = 0) : QLocalServer(parent) + { + } + +public slots: + bool listen(const QString &name) { + if (!QLocalServer::listen(name)) { + if (serverError() == QAbstractSocket::AddressInUseError) { + QFile::remove(serverName()); + return QLocalServer::listen(name); + } + return false; + } + return true; + } + + QScriptLocalSocket *nextConnection() { + QLocalSocket *other = nextPendingConnection(); + QScriptLocalSocket *s = new QScriptLocalSocket(this); + delete s->lc; + s->lc = other; + return s; + } + + bool waitForNewConnection() { + return QLocalServer::waitForNewConnection(30000); + } + + QString errorString() { + return QLocalServer::errorString(); + } + + +}; + +template +static QScriptValue _q_ScriptValueFromQObject(QScriptEngine *engine, T* const &in) +{ + return engine->newQObject(in); +} +template +static void _q_ScriptValueToQObject(const QScriptValue &v, T* &out) +{ out = qobject_cast(v.toQObject()); +} +template +static int _q_ScriptRegisterQObjectMetaType(QScriptEngine *engine, const QScriptValue &prototype) +{ + return qScriptRegisterMetaType(engine, _q_ScriptValueFromQObject, _q_ScriptValueToQObject, prototype); +} + +QT_BEGIN_NAMESPACE +Q_SCRIPT_DECLARE_QMETAOBJECT(QScriptLocalSocket, QObject*); +Q_SCRIPT_DECLARE_QMETAOBJECT(QScriptLocalServer, QObject*); +QT_END_NAMESPACE + +static void interactive(QScriptEngine &eng) +{ + QTextStream qin(stdin, QFile::ReadOnly); + + const char *qscript_prompt = "qs> "; + const char *dot_prompt = ".... "; + const char *prompt = qscript_prompt; + + QString code; + + forever { + QString line; + + printf("%s", prompt); + fflush(stdout); + + line = qin.readLine(); + if (line.isNull()) + break; + + code += line; + code += QLatin1Char('\n'); + + if (line.trimmed().isEmpty()) { + continue; + + } else if (! eng.canEvaluate(code)) { + prompt = dot_prompt; + + } else { + QScriptValue result = eng.evaluate(code); + code.clear(); + prompt = qscript_prompt; + if (!result.isUndefined()) + fprintf(stderr, "%s\n", qPrintable(result.toString())); + } + } +} +Q_DECLARE_METATYPE(QScriptLocalSocket*) +Q_DECLARE_METATYPE(QScriptLocalServer*) +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + QScriptEngine eng; + QScriptValue globalObject = eng.globalObject(); + + _q_ScriptRegisterQObjectMetaType(&eng, QScriptValue()); + + QScriptValue lss = qScriptValueFromQMetaObject(&eng); + eng.globalObject().setProperty("QScriptLocalServer", lss); + + _q_ScriptRegisterQObjectMetaType(&eng, QScriptValue()); + + QScriptValue lsc = qScriptValueFromQMetaObject(&eng); + eng.globalObject().setProperty("QScriptLocalSocket", lsc); + + if (! *++argv) { + interactive(eng); + return EXIT_SUCCESS; + } + + QStringList arguments; + for (int i = 0; i < argc - 1; ++i) + arguments << QString::fromLocal8Bit(argv[i]); + + while (!arguments.isEmpty()) { + QString fn = arguments.takeFirst(); + + if (fn == QLatin1String("-i")) { + interactive(eng); + break; + } + + QString contents; + + if (fn == QLatin1String("-")) { + QTextStream stream(stdin, QFile::ReadOnly); + contents = stream.readAll(); + } else { + QFile file(fn); + if (!file.exists()) { + fprintf(stderr, "%s doesn't exists\n", qPrintable(fn)); + return EXIT_FAILURE; + } + if (file.open(QFile::ReadOnly)) { + QTextStream stream(&file); + contents = stream.readAll(); + file.close(); + } + } + + if (contents.isEmpty()) + continue; + + if (contents[0] == '#') { + contents.prepend("//"); + QScriptValue args = eng.newArray(); + args.setProperty("0", QScriptValue(&eng, fn)); + int i = 1; + while (!arguments.isEmpty()) + args.setProperty(i++, QScriptValue(&eng, arguments.takeFirst())); + eng.currentContext()->activationObject().setProperty("args", args); + } + QScriptValue r = eng.evaluate(contents); + if (eng.hasUncaughtException()) { + int line = eng.uncaughtExceptionLineNumber(); + fprintf(stderr, "%d: %s\n\t%s\n\n", line, qPrintable(fn), qPrintable(r.toString())); + return EXIT_FAILURE; + } + if (r.isNumber()) + return r.toInt32(); + } + + return EXIT_SUCCESS; +} + +#include "main.moc" diff --git a/tests/auto/network/socket/qlocalsocket/lackey/scripts/client.js b/tests/auto/network/socket/qlocalsocket/lackey/scripts/client.js new file mode 100755 index 0000000000..76cc0b97ad --- /dev/null +++ b/tests/auto/network/socket/qlocalsocket/lackey/scripts/client.js @@ -0,0 +1,35 @@ +#/bin/qscript +function QVERIFY(x, socket) { + if (!(x)) { + throw(socket.errorString()); + } +} + +var socket = new QScriptLocalSocket; +var tries = 0; +do { + socket.serverName = "qlocalsocket_autotest"; + if ((socket.errorString() != "QLocalSocket::connectToServer: Invalid name") + && (socket.errorString() != "QLocalSocket::connectToServer: Connection refused")) + break; + socket.sleep(1); + ++tries; + print("isConnected:", socket.isConnected()); +} while ((socket.errorString() == "QLocalSocket::connectToServer: Invalid name" + || (socket.errorString() == "QlocalSocket::connectToServer: Connection refused")) + && tries < 5000); +if (tries == 5000) { + print("too many tries, exiting"); +} else { +socket.waitForConnected(); +//print("isConnected:", socket.isConnected()); +if (!socket.isConnected()) + print("Not Connected:", socket.errorString()); +socket.waitForReadyRead(); +var text = socket.readLine(); +var testLine = "test"; +QVERIFY((text == testLine), socket); +QVERIFY((socket.errorString() == "Unknown error"), socket); +socket.close(); +//print("client: exiting", text); +} diff --git a/tests/auto/network/socket/qlocalsocket/lackey/scripts/server.js b/tests/auto/network/socket/qlocalsocket/lackey/scripts/server.js new file mode 100644 index 0000000000..98a83bc9dd --- /dev/null +++ b/tests/auto/network/socket/qlocalsocket/lackey/scripts/server.js @@ -0,0 +1,19 @@ +#/bin/qscript +function QVERIFY(x, server) { + if (!(x)) { + throw(server.errorString()); + } +} +var server = new QScriptLocalServer; +QVERIFY(server.listen("qlocalsocket_autotest"), server); +var done = args[1]; +var testLine = "test"; +while (done > 0) { + QVERIFY(server.waitForNewConnection(), server); + var serverSocket = server.nextConnection(); + serverSocket.write(testLine); + QVERIFY(serverSocket.waitForBytesWritten(), serverSocket); + QVERIFY(serverSocket.errorString() == "" + ||serverSocket.errorString() == "Unknown error", serverSocket); + --done; +} diff --git a/tests/auto/network/socket/qlocalsocket/qlocalsocket.pro b/tests/auto/network/socket/qlocalsocket/qlocalsocket.pro new file mode 100644 index 0000000000..931c1e0602 --- /dev/null +++ b/tests/auto/network/socket/qlocalsocket/qlocalsocket.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs +SUBDIRS = test # lackey should be moved to the QtScript module +!wince*:!symbian: SUBDIRS += example +symbian: TARGET.CAPABILITY = NetworkServices diff --git a/tests/auto/network/socket/qlocalsocket/test/test.pro b/tests/auto/network/socket/qlocalsocket/test/test.pro new file mode 100644 index 0000000000..b2755b5411 --- /dev/null +++ b/tests/auto/network/socket/qlocalsocket/test/test.pro @@ -0,0 +1,50 @@ +load(qttest_p4) + +DEFINES += QLOCALSERVER_DEBUG +DEFINES += QLOCALSOCKET_DEBUG + +symbian { + # nothing +} else:wince* { + DEFINES += QT_LOCALSOCKET_TCP + DEFINES += SRCDIR=\\\"../\\\" +} else { + DEFINES += SRCDIR=\\\"$$PWD/../\\\" +} + +QT = core network + +SOURCES += ../tst_qlocalsocket.cpp + +TARGET = tst_qlocalsocket +CONFIG(debug_and_release) { + CONFIG(debug, debug|release) { + DESTDIR = ../debug + } else { + DESTDIR = ../release + } +} else { + DESTDIR = .. +} + +wince* { + additionalFiles.files = ../lackey/lackey.exe + additionalFiles.path = lackey +} + +symbian { + additionalFiles.files = lackey.exe + additionalFiles.path = \\sys\\bin + TARGET.UID3 = 0xE0340005 + DEFINES += SYMBIAN_SRCDIR_UID=$$lower($$replace(TARGET.UID3,"0x","")) +} + +wince*|symbian { + scriptFiles.files = ../lackey/scripts/*.js + scriptFiles.path = lackey/scripts + DEPLOYMENT += additionalFiles scriptFiles + QT += script # for easy deployment of QtScript + + requires(contains(QT_CONFIG,script)) +} + diff --git a/tests/auto/network/socket/qlocalsocket/tst_qlocalsocket.cpp b/tests/auto/network/socket/qlocalsocket/tst_qlocalsocket.cpp new file mode 100644 index 0000000000..57daa92f82 --- /dev/null +++ b/tests/auto/network/socket/qlocalsocket/tst_qlocalsocket.cpp @@ -0,0 +1,1120 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include + +#include +#include +#include +#include "../../../../shared/util.h" + +#ifdef Q_OS_SYMBIAN + #include +#endif +//TESTED_CLASS=QLocalServer, QLocalSocket +//TESTED_FILES=network/socket/qlocalserver.cpp network/socket/qlocalsocket.cpp +#ifdef Q_OS_SYMBIAN + #define STRINGIFY(x) #x + #define TOSTRING(x) STRINGIFY(x) + #define SRCDIR "C:/Private/" TOSTRING(SYMBIAN_SRCDIR_UID) "/" +#endif +Q_DECLARE_METATYPE(QLocalSocket::LocalSocketError) +Q_DECLARE_METATYPE(QLocalSocket::LocalSocketState) + +class tst_QLocalSocket : public QObject +{ + Q_OBJECT + +public: + tst_QLocalSocket(); + virtual ~tst_QLocalSocket(); + +public Q_SLOTS: + void init(); + void cleanup(); + +private slots: + // basics + void server_basic(); + void server_connectionsCount(); + void socket_basic(); + + void listen_data(); + void listen(); + + void listenAndConnect_data(); + void listenAndConnect(); + + void sendData_data(); + void sendData(); + + void readBufferOverflow(); + + void fullPath(); + + void hitMaximumConnections_data(); + void hitMaximumConnections(); + + void setSocketDescriptor(); + + void threadedConnection_data(); + void threadedConnection(); + + void processConnection_data(); + void processConnection(); + + void longPath(); + void waitForDisconnect(); + void waitForDisconnectByServer(); + + void removeServer(); + + void recycleServer(); + + void multiConnect(); + void writeOnlySocket(); + void writeToClientAndDisconnect(); + void debug(); + void bytesWrittenSignal(); + void syncDisconnectNotify(); + void asyncDisconnectNotify(); + +#ifdef Q_OS_SYMBIAN +private: + void unlink(QString serverName); +#endif +}; + +tst_QLocalSocket::tst_QLocalSocket() +{ + if (!QFile::exists("lackey/lackey" +#ifdef Q_OS_WIN + ".exe" +#endif + )) + qWarning() << "lackey executable doesn't exists!"; +} + +tst_QLocalSocket::~tst_QLocalSocket() +{ +} + +void tst_QLocalSocket::init() +{ + qRegisterMetaType("QLocalSocket::LocalSocketState"); + qRegisterMetaType("QLocalSocket::LocalSocketError"); +} + +void tst_QLocalSocket::cleanup() +{ +} + +class LocalServer : public QLocalServer +{ + Q_OBJECT + +public: + LocalServer() : QLocalServer() + { + connect(this, SIGNAL(newConnection()), this, SLOT(slotNewConnection())); + } + + bool listen(const QString &name) + { + removeServer(name); + return QLocalServer::listen(name); + } + + QList hits; + +protected: + void incomingConnection(quintptr socketDescriptor) + { + hits.append(socketDescriptor); + QLocalServer::incomingConnection(socketDescriptor); + } + +private slots: + void slotNewConnection() { + QVERIFY(!hits.isEmpty()); + QVERIFY(hasPendingConnections()); + } +}; + +class LocalSocket : public QLocalSocket +{ + Q_OBJECT + +public: + LocalSocket(QObject *parent = 0) : QLocalSocket(parent) + { + connect(this, SIGNAL(connected()), + this, SLOT(slotConnected())); + connect(this, SIGNAL(disconnected()), + this, SLOT(slotDisconnected())); + connect(this, SIGNAL(error(QLocalSocket::LocalSocketError)), + this, SLOT(slotError(QLocalSocket::LocalSocketError))); + connect(this, SIGNAL(stateChanged(QLocalSocket::LocalSocketState)), + this, SLOT(slotStateChanged(QLocalSocket::LocalSocketState))); + connect(this, SIGNAL(readyRead()), + this, SLOT(slotReadyRead())); + } + +private slots: + void slotConnected() + { + QCOMPARE(state(), QLocalSocket::ConnectedState); + } + void slotDisconnected() + { + QCOMPARE(state(), QLocalSocket::UnconnectedState); + } + void slotError(QLocalSocket::LocalSocketError newError) + { + QVERIFY(errorString() != "Unknown error"); + QCOMPARE(error(), newError); + } + void slotStateChanged(QLocalSocket::LocalSocketState newState) + { + QCOMPARE(state(), newState); + } + void slotReadyRead() + { + QVERIFY(bytesAvailable() > 0); + } +}; + +// basic test make sure no segfaults and check default values +void tst_QLocalSocket::server_basic() +{ + LocalServer server; + QSignalSpy spyNewConnection(&server, SIGNAL(newConnection())); + server.close(); + QCOMPARE(server.errorString(), QString()); + QCOMPARE(server.hasPendingConnections(), false); + QCOMPARE(server.isListening(), false); + QCOMPARE(server.maxPendingConnections(), 30); + QCOMPARE(server.nextPendingConnection(), (QLocalSocket*)0); + QCOMPARE(server.serverName(), QString()); + QCOMPARE(server.fullServerName(), QString()); + QCOMPARE(server.serverError(), QAbstractSocket::UnknownSocketError); + server.setMaxPendingConnections(20); + bool timedOut = true; + QCOMPARE(server.waitForNewConnection(3000, &timedOut), false); + QVERIFY(!timedOut); + QCOMPARE(server.listen(QString()), false); + + QCOMPARE(server.hits.count(), 0); + QCOMPARE(spyNewConnection.count(), 0); +} + +void tst_QLocalSocket::server_connectionsCount() +{ + LocalServer server; + server.setMaxPendingConnections(10); + QCOMPARE(server.maxPendingConnections(), 10); +} + +// basic test make sure no segfaults and check default values +void tst_QLocalSocket::socket_basic() +{ + LocalSocket socket; + QSignalSpy spyConnected(&socket, SIGNAL(connected())); + QSignalSpy spyDisconnected(&socket, SIGNAL(disconnected())); + QSignalSpy spyError(&socket, SIGNAL(error(QLocalSocket::LocalSocketError))); + QSignalSpy spyStateChanged(&socket, SIGNAL(stateChanged(QLocalSocket::LocalSocketState))); + QSignalSpy spyReadyRead(&socket, SIGNAL(readyRead())); + + QCOMPARE(socket.serverName(), QString()); + QCOMPARE(socket.fullServerName(), QString()); + socket.abort(); + QVERIFY(socket.bytesAvailable() == 0); + QVERIFY(socket.bytesToWrite() == 0); + QCOMPARE(socket.canReadLine(), false); + socket.close(); + socket.disconnectFromServer(); + QCOMPARE(QLocalSocket::UnknownSocketError, socket.error()); + QVERIFY(socket.errorString() != QString()); + QCOMPARE(socket.flush(), false); + QCOMPARE(socket.isValid(), false); + QVERIFY(socket.readBufferSize() == 0); + socket.setReadBufferSize(0); + //QCOMPARE(socket.socketDescriptor(), -1); + QCOMPARE(socket.state(), QLocalSocket::UnconnectedState); + QCOMPARE(socket.waitForConnected(0), false); + QCOMPARE(socket.waitForDisconnected(0), false); + QCOMPARE(socket.waitForReadyRead(0), false); + + QCOMPARE(spyConnected.count(), 0); + QCOMPARE(spyDisconnected.count(), 0); + QCOMPARE(spyError.count(), 0); + QCOMPARE(spyStateChanged.count(), 0); + QCOMPARE(spyReadyRead.count(), 0); +} + +void tst_QLocalSocket::listen_data() +{ + QTest::addColumn("name"); + QTest::addColumn("canListen"); + QTest::addColumn("close"); + QTest::newRow("null") << QString() << false << false; + QTest::newRow("tst_localsocket") << "tst_localsocket" << true << true; + QTest::newRow("tst_localsocket") << "tst_localsocket" << true << false; +} + +// start a server that listens, but don't connect a socket, make sure everything is in order +void tst_QLocalSocket::listen() +{ + LocalServer server; + QSignalSpy spyNewConnection(&server, SIGNAL(newConnection())); + + QFETCH(QString, name); +#ifdef Q_OS_SYMBIAN + unlink(name); +#endif + QFETCH(bool, canListen); + QFETCH(bool, close); + QVERIFY2((server.listen(name) == canListen), server.errorString().toLatin1().constData()); + + // test listening + QCOMPARE(server.serverName(), name); + QVERIFY(server.fullServerName().contains(name)); + QCOMPARE(server.isListening(), canListen); + QCOMPARE(server.hasPendingConnections(), false); + QCOMPARE(server.nextPendingConnection(), (QLocalSocket*)0); + QCOMPARE(server.hits.count(), 0); + QCOMPARE(spyNewConnection.count(), 0); + if (canListen) { + QVERIFY(server.errorString() == QString()); + QCOMPARE(server.serverError(), QAbstractSocket::UnknownSocketError); + // already isListening + QVERIFY(!server.listen(name)); + } else { + QVERIFY(server.errorString() != QString()); + QCOMPARE(server.serverError(), QAbstractSocket::HostNotFoundError); + } + QCOMPARE(server.maxPendingConnections(), 30); + bool timedOut = false; + QCOMPARE(server.waitForNewConnection(3000, &timedOut), false); + QCOMPARE(timedOut, canListen); + if (close) + server.close(); +} + +void tst_QLocalSocket::listenAndConnect_data() +{ + QTest::addColumn("name"); + QTest::addColumn("canListen"); + QTest::addColumn("connections"); + for (int i = 0; i < 3; ++i) { + int connections = i; + if (i == 2) + connections = 5; + QTest::newRow(QString("null %1").arg(i).toLatin1()) << QString() << false << connections; + QTest::newRow(QString("tst_localsocket %1").arg(i).toLatin1()) << "tst_localsocket" << true << connections; + } +} + +void tst_QLocalSocket::listenAndConnect() +{ + LocalServer server; + QSignalSpy spyNewConnection(&server, SIGNAL(newConnection())); + + QFETCH(QString, name); + QFETCH(bool, canListen); +#ifdef Q_OS_SYMBIAN + unlink(name); +#endif + QCOMPARE(server.listen(name), canListen); + QTest::qWait(1000); + //QVERIFY(!server.errorString().isEmpty()); + QCOMPARE(server.serverError(), + canListen ? QAbstractSocket::UnknownSocketError : QAbstractSocket::HostNotFoundError); + + // test creating connection(s) + QFETCH(int, connections); + QList sockets; + for (int i = 0; i < connections; ++i) { + LocalSocket *socket = new LocalSocket; + + QSignalSpy spyConnected(socket, SIGNAL(connected())); + QSignalSpy spyDisconnected(socket, SIGNAL(disconnected())); + QSignalSpy spyError(socket, SIGNAL(error(QLocalSocket::LocalSocketError))); + QSignalSpy spyStateChanged(socket, SIGNAL(stateChanged(QLocalSocket::LocalSocketState))); + QSignalSpy spyReadyRead(socket, SIGNAL(readyRead())); + + socket->connectToServer(name); +#if defined(QT_LOCALSOCKET_TCP) + QTest::qWait(250); +#endif + + QCOMPARE(socket->serverName(), name); + QVERIFY(socket->fullServerName().contains(name)); + sockets.append(socket); + if (canListen) { + QVERIFY(socket->waitForConnected()); + QVERIFY(socket->isValid()); + QCOMPARE(socket->errorString(), QString("Unknown error")); + QCOMPARE(socket->error(), QLocalSocket::UnknownSocketError); + QCOMPARE(socket->state(), QLocalSocket::ConnectedState); + //QVERIFY(socket->socketDescriptor() != -1); + QCOMPARE(spyError.count(), 0); + } else { + QVERIFY(socket->errorString() != QString()); + QVERIFY(socket->error() != QLocalSocket::UnknownSocketError); + QCOMPARE(socket->state(), QLocalSocket::UnconnectedState); + //QVERIFY(socket->socketDescriptor() == -1); + QCOMPARE(qVariantValue(spyError.first()[0]), + QLocalSocket::ServerNotFoundError); + } + + QVERIFY(socket->bytesAvailable() == 0); + QVERIFY(socket->bytesToWrite() == 0); + QCOMPARE(socket->canReadLine(), false); + QCOMPARE(socket->flush(), false); + QCOMPARE(socket->isValid(), canListen); + QCOMPARE(socket->readBufferSize(), (qint64)0); + QCOMPARE(socket->waitForConnected(0), canListen); + QCOMPARE(socket->waitForReadyRead(0), false); + + QTRY_COMPARE(spyConnected.count(), canListen ? 1 : 0); + QCOMPARE(spyDisconnected.count(), 0); + + // error signals + QVERIFY(spyError.count() >= 0); + if (canListen) { + if (spyError.count() > 0) + QCOMPARE(qVariantValue(spyError.first()[0]), + QLocalSocket::SocketTimeoutError); + } else { + QCOMPARE(qVariantValue(spyError.first()[0]), + QLocalSocket::ServerNotFoundError); + } + + // Check first and last state + QCOMPARE(qVariantValue(spyStateChanged.first()[0]), + QLocalSocket::ConnectingState); +#if 0 + for (int j = 0; j < spyStateChanged.count(); ++j) { + QLocalSocket::LocalSocketState s; + s = qVariantValue(spyStateChanged.at(j).at(0)); + qDebug() << s; + } +#endif + if (canListen) + QCOMPARE(qVariantValue(spyStateChanged.last()[0]), + QLocalSocket::ConnectedState); + QCOMPARE(spyStateChanged.count(), 2); + QCOMPARE(spyReadyRead.count(), 0); + + bool timedOut = true; + QCOMPARE(server.waitForNewConnection(3000, &timedOut), canListen); + QVERIFY(!timedOut); + QCOMPARE(server.hasPendingConnections(), canListen); + QCOMPARE(server.isListening(), canListen); + // NOTE: socket disconnecting is not tested here + + // server checks post connection + if (canListen) { + QCOMPARE(server.serverName(), name); + QVERIFY(server.fullServerName().contains(name)); + QVERIFY(server.nextPendingConnection() != (QLocalSocket*)0); + QTRY_COMPARE(server.hits.count(), i + 1); + QCOMPARE(spyNewConnection.count(), i + 1); + QVERIFY(server.errorString() == QString()); + QCOMPARE(server.serverError(), QAbstractSocket::UnknownSocketError); + } else { + QVERIFY(server.serverName().isEmpty()); + QVERIFY(server.fullServerName().isEmpty()); + QVERIFY(server.nextPendingConnection() == (QLocalSocket*)0); + QCOMPARE(spyNewConnection.count(), 0); + QCOMPARE(server.hits.count(), 0); + QVERIFY(server.errorString() != QString()); + QCOMPARE(server.serverError(), QAbstractSocket::HostNotFoundError); + } + } + qDeleteAll(sockets.begin(), sockets.end()); + + server.close(); + + QCOMPARE(server.hits.count(), (canListen ? connections : 0)); + QCOMPARE(spyNewConnection.count(), (canListen ? connections : 0)); +} + +void tst_QLocalSocket::sendData_data() +{ + listenAndConnect_data(); +} + +void tst_QLocalSocket::sendData() +{ + QFETCH(QString, name); +#ifdef Q_OS_SYMBIAN + unlink(name); +#endif + QFETCH(bool, canListen); + + LocalServer server; + QSignalSpy spy(&server, SIGNAL(newConnection())); + + QCOMPARE(server.listen(name), canListen); + + LocalSocket socket; + QSignalSpy spyConnected(&socket, SIGNAL(connected())); + QSignalSpy spyDisconnected(&socket, SIGNAL(disconnected())); + QSignalSpy spyError(&socket, SIGNAL(error(QLocalSocket::LocalSocketError))); + QSignalSpy spyStateChanged(&socket, SIGNAL(stateChanged(QLocalSocket::LocalSocketState))); + QSignalSpy spyReadyRead(&socket, SIGNAL(readyRead())); + + // test creating a connection + socket.connectToServer(name); + bool timedOut = true; + + QCOMPARE(server.waitForNewConnection(3000, &timedOut), canListen); + +#if defined(QT_LOCALSOCKET_TCP) + QTest::qWait(250); +#endif + QVERIFY(!timedOut); + QCOMPARE(spyConnected.count(), canListen ? 1 : 0); + QCOMPARE(socket.state(), canListen ? QLocalSocket::ConnectedState : QLocalSocket::UnconnectedState); + + // test sending/receiving data + if (server.hasPendingConnections()) { + QString testLine = "test"; +#ifdef Q_OS_SYMBIAN + for (int i = 0; i < 25 * 1024; ++i) +#else + for (int i = 0; i < 50000; ++i) +#endif + testLine += "a"; + QLocalSocket *serverSocket = server.nextPendingConnection(); + QVERIFY(serverSocket); + QCOMPARE(serverSocket->state(), QLocalSocket::ConnectedState); + QTextStream out(serverSocket); + QTextStream in(&socket); + out << testLine << endl; + bool wrote = serverSocket->waitForBytesWritten(3000); + + if (!socket.canReadLine()) + QVERIFY(socket.waitForReadyRead()); + + QVERIFY(socket.bytesAvailable() >= 0); + QCOMPARE(socket.bytesToWrite(), (qint64)0); + QCOMPARE(socket.flush(), false); + QCOMPARE(socket.isValid(), canListen); + QCOMPARE(socket.readBufferSize(), (qint64)0); + QCOMPARE(spyReadyRead.count(), 1); + + QVERIFY(testLine.startsWith(in.readLine())); + + QVERIFY(wrote || serverSocket->waitForBytesWritten(1000)); + + QCOMPARE(serverSocket->errorString(), QString("Unknown error")); + QCOMPARE(socket.errorString(), QString("Unknown error")); + } + + socket.disconnectFromServer(); + QCOMPARE(spyConnected.count(), canListen ? 1 : 0); + QCOMPARE(spyDisconnected.count(), canListen ? 1 : 0); + QCOMPARE(spyError.count(), canListen ? 0 : 1); + QCOMPARE(spyStateChanged.count(), canListen ? 4 : 2); + QCOMPARE(spyReadyRead.count(), canListen ? 1 : 0); + + server.close(); + + QCOMPARE(server.hits.count(), (canListen ? 1 : 0)); + QCOMPARE(spy.count(), (canListen ? 1 : 0)); +} + +void tst_QLocalSocket::readBufferOverflow() +{ + const int readBufferSize = 128; + const int dataBufferSize = readBufferSize * 2; + const QString serverName = QLatin1String("myPreciousTestServer"); + LocalServer server; + server.listen(serverName); + QVERIFY(server.isListening()); + + LocalSocket client; + client.setReadBufferSize(readBufferSize); + client.connectToServer(serverName); + + bool timedOut = true; + QVERIFY(server.waitForNewConnection(3000, &timedOut)); + QVERIFY(!timedOut); + + QCOMPARE(client.state(), QLocalSocket::ConnectedState); + QVERIFY(server.hasPendingConnections()); + + QLocalSocket* serverSocket = server.nextPendingConnection(); + char buffer[dataBufferSize]; + memset(buffer, 0, dataBufferSize); + serverSocket->write(buffer, dataBufferSize); + serverSocket->waitForBytesWritten(); + + // wait until the first 128 bytes are ready to read + QVERIFY(client.waitForReadyRead()); + QCOMPARE(client.read(buffer, readBufferSize), qint64(readBufferSize)); + // wait until the second 128 bytes are ready to read + QVERIFY(client.waitForReadyRead()); + QCOMPARE(client.read(buffer, readBufferSize), qint64(readBufferSize)); + // no more bytes available + QVERIFY(client.bytesAvailable() == 0); +} + +// QLocalSocket/Server can take a name or path, check that it works as expected +void tst_QLocalSocket::fullPath() +{ + QLocalServer server; + QString name = "qlocalsocket_pathtest"; +#if defined(Q_OS_SYMBIAN) + QString path = ""; +#elif defined(QT_LOCALSOCKET_TCP) + QString path = "QLocalServer"; +#elif defined(Q_OS_WIN) + QString path = "\\\\.\\pipe\\"; +#else + QString path = "/tmp"; +#endif + QString serverName = path + '/' + name; + QVERIFY2(server.listen(serverName), server.errorString().toLatin1().constData()); + QCOMPARE(server.serverName(), serverName); + QCOMPARE(server.fullServerName(), serverName); + + LocalSocket socket; + socket.connectToServer(serverName); + + QCOMPARE(socket.serverName(), serverName); + QCOMPARE(socket.fullServerName(), serverName); + socket.disconnectFromServer(); +#ifdef QT_LOCALSOCKET_TCP + QTest::qWait(250); +#endif + QCOMPARE(socket.serverName(), QString()); + QCOMPARE(socket.fullServerName(), QString()); +} + +void tst_QLocalSocket::hitMaximumConnections_data() +{ + QTest::addColumn("max"); + QTest::newRow("none") << 0; + QTest::newRow("1") << 1; + QTest::newRow("3") << 3; +} + +void tst_QLocalSocket::hitMaximumConnections() +{ + QFETCH(int, max); + LocalServer server; + QString name = "tst_localsocket"; +#ifdef Q_OS_SYMBIAN + unlink(name); +#endif + server.setMaxPendingConnections(max); + QVERIFY2(server.listen(name), server.errorString().toLatin1().constData()); + int connections = server.maxPendingConnections() + 1; + QList sockets; + for (int i = 0; i < connections; ++i) { + LocalSocket *socket = new LocalSocket; + sockets.append(socket); + socket->connectToServer(name); + } + bool timedOut = true; + QVERIFY(server.waitForNewConnection(3000, &timedOut)); + QVERIFY(!timedOut); + QVERIFY(server.hits.count() > 0); + qDeleteAll(sockets.begin(), sockets.end()); +} + +// check that state and mode are kept +void tst_QLocalSocket::setSocketDescriptor() +{ + LocalSocket socket; + quintptr minusOne = -1; + socket.setSocketDescriptor(minusOne, QLocalSocket::ConnectingState, QIODevice::Append); + QCOMPARE(socket.socketDescriptor(), minusOne); + QCOMPARE(socket.state(), QLocalSocket::ConnectingState); + QVERIFY((socket.openMode() & QIODevice::Append) != 0); +} + +class Client : public QThread +{ + +public: + void run() + { + QString testLine = "test"; + LocalSocket socket; + QSignalSpy spyReadyRead(&socket, SIGNAL(readyRead())); + socket.connectToServer("qlocalsocket_threadtest"); + QVERIFY(socket.waitForConnected(1000)); + + // We should *not* have this signal yet! + QCOMPARE(spyReadyRead.count(), 0); + socket.waitForReadyRead(); + QCOMPARE(spyReadyRead.count(), 1); + QTextStream in(&socket); + QCOMPARE(in.readLine(), testLine); + socket.close(); + } +}; + +class Server : public QThread +{ + +public: + int clients; + QMutex mutex; + QWaitCondition wc; + void run() + { + QString testLine = "test"; + LocalServer server; + server.setMaxPendingConnections(10); + QVERIFY2(server.listen("qlocalsocket_threadtest"), + server.errorString().toLatin1().constData()); + mutex.lock(); + wc.wakeAll(); + mutex.unlock(); + int done = clients; + while (done > 0) { + bool timedOut = true; + QVERIFY(server.waitForNewConnection(7000, &timedOut)); + QVERIFY(!timedOut); + QLocalSocket *serverSocket = server.nextPendingConnection(); + QVERIFY(serverSocket); + QTextStream out(serverSocket); + out << testLine << endl; + QCOMPARE(serverSocket->state(), QLocalSocket::ConnectedState); + QVERIFY2(serverSocket->waitForBytesWritten(), serverSocket->errorString().toLatin1().constData()); + QCOMPARE(serverSocket->errorString(), QString("Unknown error")); + --done; + delete serverSocket; + } + QCOMPARE(server.hits.count(), clients); + } +}; + +void tst_QLocalSocket::threadedConnection_data() +{ + QTest::addColumn("threads"); + QTest::newRow("1 client") << 1; + QTest::newRow("2 clients") << 2; + QTest::newRow("5 clients") << 5; +#ifndef Q_OS_WINCE + QTest::newRow("10 clients") << 10; + QTest::newRow("20 clients") << 20; +#endif +} + +void tst_QLocalSocket::threadedConnection() +{ +#ifdef Q_OS_SYMBIAN + unlink("qlocalsocket_threadtest"); +#endif + + QFETCH(int, threads); + Server server; +#if defined(Q_OS_SYMBIAN) + server.setStackSize(0x14000); +#endif + server.clients = threads; + server.mutex.lock(); + server.start(); + server.wc.wait(&server.mutex); + + QList clients; + for (int i = 0; i < threads; ++i) { + clients.append(new Client()); +#if defined(Q_OS_SYMBIAN) + clients.last()->setStackSize(0x14000); +#endif + clients.last()->start(); + } + + server.wait(); + while (!clients.isEmpty()) { + QVERIFY(clients.first()->wait(3000)); + delete clients.takeFirst(); + } +} + +void tst_QLocalSocket::processConnection_data() +{ + QTest::addColumn("processes"); + QTest::newRow("1 client") << 1; +#ifndef Q_OS_WIN + QTest::newRow("2 clients") << 2; + QTest::newRow("5 clients") << 5; +#endif + QTest::newRow("30 clients") << 30; +} + +/*! + Create external processes that produce and consume. + */ +void tst_QLocalSocket::processConnection() +{ +#if defined(QT_NO_PROCESS) || defined(Q_CC_NOKIAX86) + QSKIP("Qt was compiled with QT_NO_PROCESS", SkipAll); +#else + QFETCH(int, processes); + QStringList serverArguments = QStringList() << SRCDIR "lackey/scripts/server.js" << QString::number(processes); + QProcess producer; + producer.setProcessChannelMode(QProcess::ForwardedChannels); +#ifdef Q_WS_QWS + serverArguments << "-qws"; +#endif + QList consumers; + producer.start("lackey/lackey", serverArguments); + QVERIFY(producer.waitForStarted(-1)); + QTest::qWait(2000); + for (int i = 0; i < processes; ++i) { + QStringList arguments = QStringList() << SRCDIR "lackey/scripts/client.js"; +#ifdef Q_WS_QWS + arguments << "-qws"; +#endif + QProcess *p = new QProcess; + p->setProcessChannelMode(QProcess::ForwardedChannels); + consumers.append(p); + p->start("lackey/lackey", arguments); + } + + while (!consumers.isEmpty()) { + consumers.first()->waitForFinished(20000); + QCOMPARE(consumers.first()->exitStatus(), QProcess::NormalExit); + QCOMPARE(consumers.first()->exitCode(), 0); + QProcess *consumer = consumers.takeFirst(); + consumer->terminate(); + delete consumer; + } + producer.waitForFinished(15000); +#endif +} + +void tst_QLocalSocket::longPath() +{ +#ifndef Q_OS_WIN + QString name; + for (int i = 0; i < 256; ++i) + name += 'a'; + LocalServer server; + QVERIFY(!server.listen(name)); + + LocalSocket socket; + socket.connectToServer(name); + QCOMPARE(socket.state(), QLocalSocket::UnconnectedState); +#endif +} + +void tst_QLocalSocket::waitForDisconnect() +{ + QString name = "tst_localsocket"; +#ifdef Q_OS_SYMBIAN + unlink(name); +#endif + LocalServer server; + QVERIFY(server.listen(name)); + LocalSocket socket; + socket.connectToServer(name); + QVERIFY(socket.waitForConnected(3000)); + QVERIFY(server.waitForNewConnection(3000)); + QLocalSocket *serverSocket = server.nextPendingConnection(); + QVERIFY(serverSocket); + socket.disconnectFromServer(); + QTime timer; + timer.start(); + QVERIFY(serverSocket->waitForDisconnected(3000)); + QVERIFY(timer.elapsed() < 2000); +} + +void tst_QLocalSocket::waitForDisconnectByServer() +{ + QString name = "tst_localsocket"; + LocalServer server; + QVERIFY(server.listen(name)); + LocalSocket socket; + QSignalSpy spy(&socket, SIGNAL(disconnected())); + QVERIFY(spy.isValid()); + socket.connectToServer(name); + QVERIFY(socket.waitForConnected(3000)); + QVERIFY(server.waitForNewConnection(3000)); + QLocalSocket *serverSocket = server.nextPendingConnection(); + QVERIFY(serverSocket); + serverSocket->close(); + QVERIFY(serverSocket->state() == QLocalSocket::UnconnectedState); + QVERIFY(socket.waitForDisconnected(3000)); + QCOMPARE(spy.count(), 1); +} + +void tst_QLocalSocket::removeServer() +{ + // this is a hostile takeover, but recovering from a crash results in the same + QLocalServer server, server2; + QVERIFY(QLocalServer::removeServer("cleanuptest")); + QVERIFY(server.listen("cleanuptest")); +#ifndef Q_OS_WIN + // on Windows, there can be several sockets listening on the same pipe + // on Unix, there can only be one socket instance + QVERIFY(! server2.listen("cleanuptest")); +#endif + QVERIFY(QLocalServer::removeServer("cleanuptest")); + QVERIFY(server2.listen("cleanuptest")); +} + +void tst_QLocalSocket::recycleServer() +{ +#ifdef Q_OS_SYMBIAN + unlink("recycletest1"); +#endif + + QLocalServer server; + QLocalSocket client; + + QVERIFY(server.listen("recycletest1")); + client.connectToServer("recycletest1"); + QVERIFY(client.waitForConnected(201)); + QVERIFY(server.waitForNewConnection(201)); + QVERIFY(server.nextPendingConnection() != 0); + + server.close(); + client.disconnectFromServer(); + qApp->processEvents(); + + QVERIFY(server.listen("recycletest2")); + client.connectToServer("recycletest2"); + QVERIFY(client.waitForConnected(202)); + QVERIFY(server.waitForNewConnection(202)); + QVERIFY(server.nextPendingConnection() != 0); +} + +void tst_QLocalSocket::multiConnect() +{ + QLocalServer server; + QLocalSocket client1; + QLocalSocket client2; + QLocalSocket client3; + + QVERIFY(server.listen("multiconnect")); + + client1.connectToServer("multiconnect"); + client2.connectToServer("multiconnect"); + client3.connectToServer("multiconnect"); + + QVERIFY(client1.waitForConnected(201)); + QVERIFY(client2.waitForConnected(202)); + QVERIFY(client3.waitForConnected(203)); + + QVERIFY(server.waitForNewConnection(201)); + QVERIFY(server.nextPendingConnection() != 0); + QVERIFY(server.waitForNewConnection(202)); + QVERIFY(server.nextPendingConnection() != 0); + QVERIFY(server.waitForNewConnection(203)); + QVERIFY(server.nextPendingConnection() != 0); +} + +void tst_QLocalSocket::writeOnlySocket() +{ + QLocalServer server; +#ifdef Q_OS_SYMBIAN + unlink("writeOnlySocket"); +#endif + QVERIFY(server.listen("writeOnlySocket")); + + QLocalSocket client; + client.connectToServer("writeOnlySocket", QIODevice::WriteOnly); + QVERIFY(client.waitForConnected()); +#if defined(Q_OS_SYMBIAN) + QTest::qWait(250); +#endif + QVERIFY(server.waitForNewConnection(200)); + QLocalSocket* serverSocket = server.nextPendingConnection(); + QVERIFY(serverSocket); + + QCOMPARE(client.bytesAvailable(), qint64(0)); + QCOMPARE(client.state(), QLocalSocket::ConnectedState); +} + +void tst_QLocalSocket::writeToClientAndDisconnect() +{ +#ifdef Q_OS_SYMBIAN + unlink("writeAndDisconnectServer"); +#endif + + QLocalServer server; + QLocalSocket client; + QSignalSpy readChannelFinishedSpy(&client, SIGNAL(readChannelFinished())); + + QVERIFY(server.listen("writeAndDisconnectServer")); + client.connectToServer("writeAndDisconnectServer"); + QVERIFY(client.waitForConnected(200)); + QVERIFY(server.waitForNewConnection(200)); + QLocalSocket* clientSocket = server.nextPendingConnection(); + QVERIFY(clientSocket); + + char buffer[100]; + memset(buffer, 0, sizeof(buffer)); + QCOMPARE(clientSocket->write(buffer, sizeof(buffer)), (qint64)sizeof(buffer)); + clientSocket->waitForBytesWritten(); + clientSocket->close(); + server.close(); + + QTRY_COMPARE(readChannelFinishedSpy.count(), 1); + QCOMPARE(client.read(buffer, sizeof(buffer)), (qint64)sizeof(buffer)); + client.waitForDisconnected(); + QCOMPARE(client.state(), QLocalSocket::UnconnectedState); +} + +void tst_QLocalSocket::debug() +{ + // Make sure this compiles + qDebug() << QLocalSocket::ConnectionRefusedError << QLocalSocket::UnconnectedState; +} + +class WriteThread : public QThread +{ +Q_OBJECT +public: + void run() { + QLocalSocket socket; + socket.connectToServer("qlocalsocket_readyread"); + + if (!socket.waitForConnected(3000)) + exec(); + connect(&socket, SIGNAL(bytesWritten(qint64)), + this, SLOT(bytesWritten(qint64)), Qt::QueuedConnection); + socket.write("testing\n"); + exec(); + } +public slots: + void bytesWritten(qint64) { + exit(); + } + +private: +}; + +/* + Tests the emission of the bytesWritten(qint64) + signal. + + Create a thread that will write to a socket. + If the bytesWritten(qint64) signal is generated, + the slot connected to it will exit the thread, + indicating test success. + +*/ +void tst_QLocalSocket::bytesWrittenSignal() +{ + QLocalServer server; + QVERIFY(server.listen("qlocalsocket_readyread")); + WriteThread writeThread; + writeThread.start(); + bool timedOut = false; + QVERIFY(server.waitForNewConnection(3000, &timedOut)); + QVERIFY(!timedOut); + QTest::qWait(2000); + QVERIFY(writeThread.wait(2000)); +} + +void tst_QLocalSocket::syncDisconnectNotify() +{ +#ifdef Q_OS_SYMBIAN + unlink("syncDisconnectNotify"); +#endif + + QLocalServer server; + QVERIFY(server.listen("syncDisconnectNotify")); + QLocalSocket client; + client.connectToServer("syncDisconnectNotify"); + QVERIFY(server.waitForNewConnection()); + QLocalSocket* serverSocket = server.nextPendingConnection(); + QVERIFY(serverSocket); + delete serverSocket; + QCOMPARE(client.waitForReadyRead(), false); +} + +void tst_QLocalSocket::asyncDisconnectNotify() +{ +#ifdef Q_OS_SYMBIAN + unlink("asyncDisconnectNotify"); +#endif + + QLocalServer server; + QVERIFY(server.listen("asyncDisconnectNotify")); + QLocalSocket client; + QSignalSpy disconnectedSpy(&client, SIGNAL(disconnected())); + client.connectToServer("asyncDisconnectNotify"); + QVERIFY(server.waitForNewConnection()); + QLocalSocket* serverSocket = server.nextPendingConnection(); + QVERIFY(serverSocket); + delete serverSocket; + QTRY_VERIFY(!disconnectedSpy.isEmpty()); +} + +#ifdef Q_OS_SYMBIAN +void tst_QLocalSocket::unlink(QString name) +{ + if(name.length() == 0) + return; + + QString fullName; + // determine the full server path + if (name.startsWith(QLatin1Char('/'))) { + fullName = name; + } else { + fullName = QDir::cleanPath(QDir::tempPath()); + fullName += QLatin1Char('/') + name; + fullName = QDir::toNativeSeparators(fullName); + } + + int result = ::unlink(fullName.toUtf8().data()); + + if(result != 0) { + qWarning() << "Unlinking " << fullName << " failed with " << strerror(errno); + } +} +#endif +QTEST_MAIN(tst_QLocalSocket) +#include "tst_qlocalsocket.moc" + diff --git a/tests/auto/network/socket/qsocks5socketengine/.gitignore b/tests/auto/network/socket/qsocks5socketengine/.gitignore new file mode 100644 index 0000000000..7d64805f3c --- /dev/null +++ b/tests/auto/network/socket/qsocks5socketengine/.gitignore @@ -0,0 +1 @@ +tst_qsocks5socketengine diff --git a/tests/auto/network/socket/qsocks5socketengine/qsocks5socketengine.pro b/tests/auto/network/socket/qsocks5socketengine/qsocks5socketengine.pro new file mode 100644 index 0000000000..3a144aeacf --- /dev/null +++ b/tests/auto/network/socket/qsocks5socketengine/qsocks5socketengine.pro @@ -0,0 +1,17 @@ +load(qttest_p4) +SOURCES += tst_qsocks5socketengine.cpp + + +include(../platformsocketengine/platformsocketengine.pri) + + +MOC_DIR=tmp + +QT = core-private network-private + +# Symbian toolchain does not support correct include semantics +symbian:INCLUDEPATH+=..\\..\\..\\include\\QtNetwork\\private +symbian: TARGET.CAPABILITY = NetworkServices + + +requires(contains(QT_CONFIG,private_tests)) diff --git a/tests/auto/network/socket/qsocks5socketengine/tst_qsocks5socketengine.cpp b/tests/auto/network/socket/qsocks5socketengine/tst_qsocks5socketengine.cpp new file mode 100644 index 0000000000..2678816482 --- /dev/null +++ b/tests/auto/network/socket/qsocks5socketengine/tst_qsocks5socketengine.cpp @@ -0,0 +1,963 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../../network-settings.h" + +Q_DECLARE_METATYPE(QQueue) + +class tst_QSocks5SocketEngine : public QObject, public QAbstractSocketEngineReceiver +{ + Q_OBJECT + +public: + tst_QSocks5SocketEngine(); + virtual ~tst_QSocks5SocketEngine(); + + +public slots: + void init(); + void cleanup(); +private slots: + void construction(); + void errorTest_data(); + void errorTest(); + void simpleConnectToIMAP(); + void simpleErrorsAndStates(); + void udpTest(); + void serverTest(); + void tcpSocketBlockingTest(); + void tcpSocketNonBlockingTest(); + void downloadBigFile(); + // void tcpLoopbackPerformance(); + void passwordAuth(); + void passwordAuth2(); + +protected slots: + void tcpSocketNonBlocking_hostFound(); + void tcpSocketNonBlocking_connected(); + void tcpSocketNonBlocking_closed(); + void tcpSocketNonBlocking_readyRead(); + void tcpSocketNonBlocking_bytesWritten(qint64); + void exitLoopSlot(); + void downloadBigFileSlot(); + void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth); + +private: + void readNotification() { } + void writeNotification() { } + void exceptionNotification() { } + void connectionNotification() { } + QTcpSocket *tcpSocketNonBlocking_socket; + QStringList tcpSocketNonBlocking_data; + qint64 tcpSocketNonBlocking_totalWritten; + QTcpSocket *tmpSocket; + qint64 bytesAvailable; +}; + +class MiniSocks5Server: public QTcpServer +{ + Q_OBJECT +public: + QQueue responses; + + MiniSocks5Server(const QQueue r) + : responses(r) + { + listen(); + connect(this, SIGNAL(newConnection()), SLOT(handleNewConnection())); + } + +private slots: + void handleNewConnection() + { + QTcpSocket *client = nextPendingConnection(); + connect(client, SIGNAL(readyRead()), SLOT(handleClientCommand())); + client->setProperty("pendingResponses", qVariantFromValue(responses)); + } + + void handleClientCommand() + { + // WARNING + // this assumes that the client command is received in its entirety + // should be ok, since SOCKSv5 commands are rather small + QTcpSocket *client = static_cast(sender()); + QQueue pendingResponses = + qvariant_cast >(client->property("pendingResponses")); + if (pendingResponses.isEmpty()) + client->disconnectFromHost(); + else + client->write(pendingResponses.dequeue()); + client->setProperty("pendingResponses", qVariantFromValue(pendingResponses)); + } +}; + +tst_QSocks5SocketEngine::tst_QSocks5SocketEngine() +{ + Q_SET_DEFAULT_IAP +} + +tst_QSocks5SocketEngine::~tst_QSocks5SocketEngine() +{ +} + +void tst_QSocks5SocketEngine::init() +{ + tmpSocket = 0; + bytesAvailable = 0; +} + +void tst_QSocks5SocketEngine::cleanup() +{ +} + +//--------------------------------------------------------------------------- +void tst_QSocks5SocketEngine::construction() +{ + QSocks5SocketEngine socketDevice; + + QVERIFY(!socketDevice.isValid()); + + // Initialize device + QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + QVERIFY(socketDevice.isValid()); + QVERIFY(socketDevice.protocol() == QAbstractSocket::IPv4Protocol); + QVERIFY(socketDevice.socketType() == QAbstractSocket::TcpSocket); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); + // QVERIFY(socketDevice.socketDescriptor() != -1); + QVERIFY(socketDevice.localAddress() == QHostAddress()); + QVERIFY(socketDevice.localPort() == 0); + QVERIFY(socketDevice.peerAddress() == QHostAddress()); + QVERIFY(socketDevice.peerPort() == 0); + QVERIFY(socketDevice.error() == QAbstractSocket::UnknownSocketError); + + //QTest::ignoreMessage(QtWarningMsg, "QSocketLayer::bytesAvailable() was called in QAbstractSocket::UnconnectedState"); + QVERIFY(socketDevice.bytesAvailable() == 0); + + //QTest::ignoreMessage(QtWarningMsg, "QSocketLayer::hasPendingDatagrams() was called in QAbstractSocket::UnconnectedState"); + QVERIFY(!socketDevice.hasPendingDatagrams()); +} + +//--------------------------------------------------------------------------- +void tst_QSocks5SocketEngine::errorTest_data() +{ + QTest::addColumn("hostname"); + QTest::addColumn("port"); + QTest::addColumn("username"); + QTest::addColumn >("responses"); + QTest::addColumn("expectedError"); + + QQueue responses; + QTest::newRow("proxy-host-not-found") << "this-host-does-not-exist." << 1080 << QString() + << responses + << int(QAbstractSocket::ProxyNotFoundError); + QTest::newRow("proxy-connection-refused") << "127.0.0.1" << 2 << QString() + << responses + << int(QAbstractSocket::ProxyConnectionRefusedError); + +#define REPLY(name, contents) \ + static const char raw_ ## name [] = contents; \ + const QByteArray name = QByteArray::fromRawData(raw_ ## name, sizeof raw_ ## name - 1) + + REPLY(garbage, "\4\4\4\4"); + // authentication method replies + REPLY(noAuthentication, "\5\0"); + REPLY(passwordAuthentication, "\5\2"); + REPLY(garbageAuthentication, "\5\177"); + REPLY(noAcceptableAuthentication, "\5\377"); + // authentication replies + REPLY(authenticationAccepted, "\5\0"); + REPLY(authenticationNotAccepted, "\5\1"); + // connection replies + REPLY(connectionAccepted, "\5\0\0\4\177\0\0\1\0\100"); + REPLY(connectionNotAllowed, "\5\2\0"); + REPLY(networkUnreachable, "\5\3\0"); + REPLY(hostUnreachable, "\5\4\0"); + REPLY(connectionRefused, "\5\5\0"); + +#undef REPLY + + responses << garbage; + QTest::newRow("garbage1") << QString() << 0 << QString() << responses + << int(QAbstractSocket::ProxyProtocolError); + + responses.clear(); + responses << noAuthentication << garbage; + QTest::newRow("garbage2") << QString() << 0 << QString() << responses + << int(QAbstractSocket::ProxyProtocolError); + + responses.clear(); + responses << garbageAuthentication; + QTest::newRow("unknown-auth-method") << QString() << 0 << QString() + << responses + << int(QAbstractSocket::SocketAccessError); + + responses.clear(); + responses << noAcceptableAuthentication; + QTest::newRow("no-acceptable-authentication") << QString() << 0 << QString() + << responses + << int(QAbstractSocket::ProxyAuthenticationRequiredError); + + responses.clear(); + responses << passwordAuthentication << authenticationNotAccepted; + QTest::newRow("authentication-required") << QString() << 0 << "foo" + << responses + << int(QAbstractSocket::ProxyAuthenticationRequiredError); + + responses.clear(); + responses << noAuthentication << connectionNotAllowed; + QTest::newRow("connection-not-allowed") << QString() << 0 << QString() + << responses + << int(QAbstractSocket::SocketAccessError); + + responses.clear(); + responses << noAuthentication << networkUnreachable; + QTest::newRow("network-unreachable") << QString() << 0 << QString() + << responses + << int(QAbstractSocket::NetworkError); + + responses.clear(); + responses << noAuthentication << hostUnreachable; + QTest::newRow("host-unreachable") << QString() << 0 << QString() + << responses + << int(QAbstractSocket::HostNotFoundError); + + responses.clear(); + responses << noAuthentication << connectionRefused; + QTest::newRow("connection-refused") << QString() << 0 << QString() + << responses + << int(QAbstractSocket::ConnectionRefusedError); +} + +void tst_QSocks5SocketEngine::errorTest() +{ + QFETCH(QString, hostname); + QFETCH(int, port); + QFETCH(QString, username); + QFETCH(QQueue, responses); + QFETCH(int, expectedError); + + MiniSocks5Server server(responses); + + if (hostname.isEmpty()) { + hostname = "127.0.0.1"; + port = server.serverPort(); + } + QTcpSocket socket; + socket.setProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, hostname, port, username, username)); + socket.connectToHost("0.1.2.3", 12345); + + connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), + &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(int(socket.error()), expectedError); +} + +//--------------------------------------------------------------------------- +void tst_QSocks5SocketEngine::simpleConnectToIMAP() +{ + QSocks5SocketEngine socketDevice; + + // Initialize device + QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); + + socketDevice.setProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080)); + + QVERIFY(!socketDevice.connectToHost(QtNetworkSettings::serverIP(), 143)); + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectingState); + QVERIFY(socketDevice.waitForWrite()); + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectedState); + QVERIFY(socketDevice.peerAddress() == QtNetworkSettings::serverIP()); + + // Wait for the greeting + QVERIFY(socketDevice.waitForRead()); + + // Read the greeting + qint64 available = socketDevice.bytesAvailable(); + QVERIFY(available > 0); + QByteArray array; + array.resize(available); + QVERIFY(socketDevice.read(array.data(), array.size()) == available); + + // Check that the greeting is what we expect it to be + QVERIFY2(QtNetworkSettings::compareReplyIMAP(array), array.constData()); + + // Write a logout message + QByteArray array2 = "XXXX LOGOUT\r\n"; + QVERIFY(socketDevice.write(array2.data(), + array2.size()) == array2.size()); + + // Wait for the response + QVERIFY(socketDevice.waitForRead()); + + available = socketDevice.bytesAvailable(); + QVERIFY(available > 0); + array.resize(available); + QVERIFY(socketDevice.read(array.data(), array.size()) == available); + + // Check that the greeting is what we expect it to be + QCOMPARE(array.constData(), "* BYE LOGOUT received\r\nXXXX OK Completed\r\n"); + + // Wait for the response + QVERIFY(socketDevice.waitForRead()); + char c; + QVERIFY(socketDevice.read(&c, sizeof(c)) == -1); + QVERIFY(socketDevice.error() == QAbstractSocket::RemoteHostClosedError); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); +} + +//--------------------------------------------------------------------------- +void tst_QSocks5SocketEngine::simpleErrorsAndStates() +{ + { + QSocks5SocketEngine socketDevice; + + // Initialize device + QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + + socketDevice.setProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080)); + + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); + QVERIFY(!socketDevice.connectToHost(QHostInfo::fromName(QtNetworkSettings::serverName()).addresses().first(), 8088)); + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectingState); + if (socketDevice.waitForWrite(15000)) { + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState || + socketDevice.state() == QAbstractSocket::ConnectedState); + } else { + QVERIFY(socketDevice.error() == QAbstractSocket::SocketTimeoutError); + } + } + +} + +/* +//--------------------------------------------------------------------------- +void tst_QSocks5SocketEngine::tcpLoopbackPerformance() +{ + QTcpServer server; + + // Bind to any port on all interfaces + QVERIFY(server.bind(QHostAddress("0.0.0.0"), 0)); + QVERIFY(server.state() == QAbstractSocket::BoundState); + quint16 port = server.localPort(); + + // Listen for incoming connections + QVERIFY(server.listen()); + QVERIFY(server.state() == QAbstractSocket::ListeningState); + + // Initialize a Tcp socket + QSocks5SocketEngine client; + QVERIFY(client.initialize(QAbstractSocket::TcpSocket)); + + client.setProxy(QHostAddress("80.232.37.158"), 1081); + + // Connect to our server + if (!client.connectToHost(QHostAddress("127.0.0.1"), port)) { + QVERIFY(client.waitForWrite()); + QVERIFY(client.connectToHost(QHostAddress("127.0.0.1"), port)); + } + + // The server accepts the connectio + int socketDescriptor = server.accept(); + QVERIFY(socketDescriptor > 0); + + // A socket device is initialized on the server side, passing the + // socket descriptor from accept(). It's pre-connected. + QSocketLayer serverSocket; + QVERIFY(serverSocket.initialize(socketDescriptor)); + QVERIFY(serverSocket.state() == QAbstractSocket::ConnectedState); + + const int messageSize = 1024 * 256; + QByteArray message1(messageSize, '@'); + QByteArray answer(messageSize, '@'); + + QTime timer; + timer.start(); + qlonglong readBytes = 0; + while (timer.elapsed() < 5000) { + qlonglong written = serverSocket.write(message1.data(), message1.size()); + while (written > 0) { + client.waitForRead(); + if (client.bytesAvailable() > 0) { + qlonglong readNow = client.read(answer.data(), answer.size()); + written -= readNow; + readBytes += readNow; + } + } + } + + qDebug("\t\t%.1fMB/%.1fs: %.1fMB/s", + readBytes / (1024.0 * 1024.0), + timer.elapsed() / 1024.0, + (readBytes / (timer.elapsed() / 1000.0)) / (1024 * 1024)); +} +*/ + +//--------------------------------------------------------------------------- +void tst_QSocks5SocketEngine::serverTest() +{ + QSocks5SocketEngine server; + + // Initialize a Tcp socket + QVERIFY(server.initialize(QAbstractSocket::TcpSocket)); + + QNetworkProxy proxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080); + + server.setProxy(proxy); + + // Bind to any port on all interfaces + QVERIFY(server.bind(QHostAddress("0.0.0.0"), 0)); + QVERIFY(server.state() == QAbstractSocket::BoundState); + + // Listen for incoming connections + QVERIFY(server.listen()); + QVERIFY(server.state() == QAbstractSocket::ListeningState); + + // Initialize a Tcp socket + QSocks5SocketEngine client; + QVERIFY(client.initialize(QAbstractSocket::TcpSocket)); + + client.setProxy(proxy); + + // QTest::wait(100000); // ### timing problem on win32 + + + // Connect to our server + if (!client.connectToHost(server.localAddress(), server.localPort())) { + QVERIFY(client.waitForWrite()); + // QTest::wait(100); // ### timing problem on win32 + QVERIFY(client.state() == QAbstractSocket::ConnectedState); + //QTest::wait(100); + } + + QVERIFY(server.waitForRead()); + + // The server accepts the connection + int socketDescriptor = server.accept(); + QVERIFY(socketDescriptor > 0); + + // A socket device is initialized on the server side, passing the + // socket descriptor from accept(). It's pre-connected. + + QSocks5SocketEngine serverSocket; + QVERIFY(serverSocket.initialize(socketDescriptor)); + QVERIFY(serverSocket.state() == QAbstractSocket::ConnectedState); + + QVERIFY(serverSocket.localAddress() == client.peerAddress()); + QVERIFY(serverSocket.localPort() == client.peerPort()); + // this seems depends on the socks server implementation, especially + // when connecting /to/ the socks server /through/ the same socks server + //QVERIFY(serverSocket.peerAddress() == client.localAddress()); + //QVERIFY(serverSocket.peerPort() == client.localPort()); + + // The server socket sends a greeting to the client + QByteArray greeting = "Greetings!"; + QVERIFY(serverSocket.write(greeting.data(), + greeting.size()) == greeting.size()); + + // The client waits for the greeting to arrive + QVERIFY(client.waitForRead()); + qint64 available = client.bytesAvailable(); + QVERIFY(available > 0); + + // The client reads the greeting and checks that it's correct + QByteArray response; + response.resize(available); + QVERIFY(client.read(response.data(), + response.size()) == response.size()); + QCOMPARE(response, greeting); +} + + +//--------------------------------------------------------------------------- +void tst_QSocks5SocketEngine::udpTest() +{ +#ifdef SYMBIAN_WINSOCK_CONNECTIVITY + QSKIP("UDP works bads on non WinPCAP emulator setting", SkipAll); +#endif + + QSocks5SocketEngine udpSocket; + + // Initialize device #1 + QVERIFY(udpSocket.initialize(QAbstractSocket::UdpSocket)); + QVERIFY(udpSocket.isValid()); + + QNetworkProxy proxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080); + + udpSocket.setProxy(proxy); + + QVERIFY(udpSocket.protocol() == QAbstractSocket::IPv4Protocol); + QVERIFY(udpSocket.socketType() == QAbstractSocket::UdpSocket); + QVERIFY(udpSocket.state() == QAbstractSocket::UnconnectedState); + + // Bind #1 + QVERIFY(udpSocket.bind(QHostAddress("0.0.0.0"), 0)); + QVERIFY(udpSocket.state() == QAbstractSocket::BoundState); + QVERIFY(udpSocket.localPort() != 0); + + // Initialize device #2 + QSocks5SocketEngine udpSocket2; + QVERIFY(udpSocket2.initialize(QAbstractSocket::UdpSocket)); + + udpSocket2.setProxy(proxy); + + // Connect device #2 to #1 + QVERIFY(udpSocket2.connectToHost(udpSocket.localAddress(), udpSocket.localPort())); + QVERIFY(udpSocket2.state() == QAbstractSocket::ConnectedState); + + // Write a message to #1 + QByteArray message1 = "hei der"; + QVERIFY(udpSocket2.write(message1.data(), + message1.size()) == message1.size()); + + // Read the message from #2 + QVERIFY(udpSocket.waitForRead()); + QVERIFY(udpSocket.hasPendingDatagrams()); + qint64 available = udpSocket.pendingDatagramSize(); + QVERIFY(available > 0); + QByteArray answer; + answer.resize(available); + QHostAddress senderAddress; + quint16 senderPort = 0; + QVERIFY(udpSocket.readDatagram(answer.data(), answer.size(), + &senderAddress, + &senderPort) == message1.size()); + QVERIFY(senderAddress == udpSocket2.localAddress()); + QVERIFY(senderPort == udpSocket2.localPort()); +} + +void tst_QSocks5SocketEngine::tcpSocketBlockingTest() +{ + QSocks5SocketEngineHandler socks5; + + QTcpSocket socket; + + // Connect + socket.connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket.waitForConnected()); + QCOMPARE(socket.state(), QTcpSocket::ConnectedState); + + // Read greeting + QVERIFY(socket.waitForReadyRead(5000)); + QString s = socket.readLine(); + QVERIFY2(QtNetworkSettings::compareReplyIMAP(s.toLatin1()), s.toLatin1().constData()); + + // Write NOOP + QCOMPARE((int) socket.write("1 NOOP\r\n", 8), 8); + + if (!socket.canReadLine()) + QVERIFY(socket.waitForReadyRead(5000)); + + // Read response + s = socket.readLine(); + QCOMPARE(s.toLatin1().constData(), "1 OK Completed\r\n"); + + // Write LOGOUT + QCOMPARE((int) socket.write("2 LOGOUT\r\n", 10), 10); + + if (!socket.canReadLine()) + QVERIFY(socket.waitForReadyRead(5000)); + + // Read two lines of respose + s = socket.readLine(); + QCOMPARE(s.toLatin1().constData(), "* BYE LOGOUT received\r\n"); + + if (!socket.canReadLine()) + QVERIFY(socket.waitForReadyRead(5000)); + + s = socket.readLine(); + QCOMPARE(s.toLatin1().constData(), "2 OK Completed\r\n"); + + // Close the socket + socket.close(); + + // Check that it's closed + QCOMPARE(socket.state(), QTcpSocket::UnconnectedState); +} + +//---------------------------------------------------------------------------------- + +void tst_QSocks5SocketEngine::tcpSocketNonBlockingTest() +{ + QSocks5SocketEngineHandler socks5; + + QTcpSocket socket; + connect(&socket, SIGNAL(hostFound()), SLOT(tcpSocketNonBlocking_hostFound())); + connect(&socket, SIGNAL(connected()), SLOT(tcpSocketNonBlocking_connected())); + connect(&socket, SIGNAL(disconnected()), SLOT(tcpSocketNonBlocking_closed())); + connect(&socket, SIGNAL(bytesWritten(qint64)), SLOT(tcpSocketNonBlocking_bytesWritten(qint64))); + connect(&socket, SIGNAL(readyRead()), SLOT(tcpSocketNonBlocking_readyRead())); + tcpSocketNonBlocking_socket = &socket; + + // Connect + socket.connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket.state() == QTcpSocket::HostLookupState || + socket.state() == QTcpSocket::ConnectingState); + + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) { + QFAIL("Timed out"); + } + + if (socket.state() == QTcpSocket::ConnectingState) { + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) { + QFAIL("Timed out"); + } + } + + QCOMPARE(socket.state(), QTcpSocket::ConnectedState); + + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) { + QFAIL("Timed out"); + } + + // Read greeting + QVERIFY(!tcpSocketNonBlocking_data.isEmpty()); + QByteArray data = tcpSocketNonBlocking_data.at(0).toLatin1(); + QVERIFY2(QtNetworkSettings::compareReplyIMAP(data), data.constData()); + + tcpSocketNonBlocking_data.clear(); + + tcpSocketNonBlocking_totalWritten = 0; + + // Write NOOP + QCOMPARE((int) socket.write("1 NOOP\r\n", 8), 8); + + + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) { + QFAIL("Timed out"); + } + + QVERIFY(tcpSocketNonBlocking_totalWritten == 8); + + + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) { + QFAIL("Timed out"); + } + + // Read response + QVERIFY(!tcpSocketNonBlocking_data.isEmpty()); + QCOMPARE(tcpSocketNonBlocking_data.at(0).toLatin1().constData(), "1 OK Completed\r\n"); + tcpSocketNonBlocking_data.clear(); + + + tcpSocketNonBlocking_totalWritten = 0; + + // Write LOGOUT + QCOMPARE((int) socket.write("2 LOGOUT\r\n", 10), 10); + + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) { + QFAIL("Timed out"); + } + + QVERIFY(tcpSocketNonBlocking_totalWritten == 10); + + // Wait for greeting + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) { + QFAIL("Timed out"); + } + + // Read two lines of respose + QCOMPARE(tcpSocketNonBlocking_data.at(0).toLatin1().constData(), "* BYE LOGOUT received\r\n"); + QCOMPARE(tcpSocketNonBlocking_data.at(1).toLatin1().constData(), "2 OK Completed\r\n"); + tcpSocketNonBlocking_data.clear(); + + // Close the socket + socket.close(); + + // Check that it's closed + QCOMPARE(socket.state(), QTcpSocket::UnconnectedState); +} + +void tst_QSocks5SocketEngine::tcpSocketNonBlocking_hostFound() +{ + QTestEventLoop::instance().exitLoop(); +} + +void tst_QSocks5SocketEngine::tcpSocketNonBlocking_connected() +{ + QTestEventLoop::instance().exitLoop(); +} + +void tst_QSocks5SocketEngine::tcpSocketNonBlocking_readyRead() +{ + while (tcpSocketNonBlocking_socket->canReadLine()) + tcpSocketNonBlocking_data.append(tcpSocketNonBlocking_socket->readLine()); + + QTestEventLoop::instance().exitLoop(); +} + +void tst_QSocks5SocketEngine::tcpSocketNonBlocking_bytesWritten(qint64 written) +{ + tcpSocketNonBlocking_totalWritten += written; + QTestEventLoop::instance().exitLoop(); +} + +void tst_QSocks5SocketEngine::tcpSocketNonBlocking_closed() +{ +} + +//---------------------------------------------------------------------------------- + +void tst_QSocks5SocketEngine::downloadBigFile() +{ + QSocks5SocketEngineHandler socks5; + + if (tmpSocket) + delete tmpSocket; + tmpSocket = new QTcpSocket; + + connect(tmpSocket, SIGNAL(connected()), SLOT(exitLoopSlot())); + connect(tmpSocket, SIGNAL(readyRead()), SLOT(downloadBigFileSlot())); + + tmpSocket->connectToHost(QtNetworkSettings::serverName(), 80); + + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) + QFAIL("Network operation timed out"); + + QByteArray hostName = QtNetworkSettings::serverName().toLatin1(); + QVERIFY(tmpSocket->state() == QAbstractSocket::ConnectedState); + QVERIFY(tmpSocket->write("GET /qtest/mediumfile HTTP/1.0\r\n") > 0); + QVERIFY(tmpSocket->write("HOST: ") > 0); + QVERIFY(tmpSocket->write(hostName.data()) > 0); + QVERIFY(tmpSocket->write("\r\n") > 0); + QVERIFY(tmpSocket->write("\r\n") > 0); + + bytesAvailable = 0; + + QTime stopWatch; + stopWatch.start(); + +#if !defined(Q_OS_WINCE) && !defined(Q_OS_SYMBIAN) + QTestEventLoop::instance().enterLoop(60); +#else + QTestEventLoop::instance().enterLoop(180); +#endif + if (QTestEventLoop::instance().timeout()) + QFAIL("Network operation timed out"); + + QCOMPARE(bytesAvailable, qint64(10000000)); + + QVERIFY(tmpSocket->state() == QAbstractSocket::ConnectedState); + + /*qDebug("\t\t%.1fMB/%.1fs: %.1fMB/s", + bytesAvailable / (1024.0 * 1024.0), + stopWatch.elapsed() / 1024.0, + (bytesAvailable / (stopWatch.elapsed() / 1000.0)) / (1024 * 1024));*/ + + delete tmpSocket; + tmpSocket = 0; +} + +void tst_QSocks5SocketEngine::exitLoopSlot() +{ + QTestEventLoop::instance().exitLoop(); +} + + +void tst_QSocks5SocketEngine::downloadBigFileSlot() +{ + QByteArray tmp=tmpSocket->readAll(); + int correction=tmp.indexOf((char)0,0); //skip header + if (correction==-1) correction=0; + bytesAvailable += (tmp.size()-correction); + if (bytesAvailable >= 10000000) + QTestEventLoop::instance().exitLoop(); +} + +void tst_QSocks5SocketEngine::passwordAuth() +{ + QSocks5SocketEngine socketDevice; + + // Initialize device + QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); + + socketDevice.setProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080, "qsockstest", "password")); + + // Connect to imap.trolltech.com's IP + QVERIFY(!socketDevice.connectToHost(QtNetworkSettings::serverIP(), 143)); + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectingState); + QVERIFY(socketDevice.waitForWrite()); + if (!socketDevice.connectToHost(QtNetworkSettings::serverIP(), 143)) { + qDebug("%d, %s", socketDevice.error(), socketDevice.errorString().toLatin1().constData()); + } + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectedState); + QVERIFY(socketDevice.peerAddress() == QtNetworkSettings::serverIP()); + + // Wait for the greeting + QVERIFY(socketDevice.waitForRead()); + + // Read the greeting + qint64 available = socketDevice.bytesAvailable(); + QVERIFY(available > 0); + QByteArray array; + array.resize(available); + QVERIFY(socketDevice.read(array.data(), array.size()) == available); + + // Check that the greeting is what we expect it to be + QVERIFY2(QtNetworkSettings::compareReplyIMAP(array), array.constData()); + + // Write a logout message + QByteArray array2 = "XXXX LOGOUT\r\n"; + QVERIFY(socketDevice.write(array2.data(), + array2.size()) == array2.size()); + + // Wait for the response + QVERIFY(socketDevice.waitForRead()); + + available = socketDevice.bytesAvailable(); + QVERIFY(available > 0); + array.resize(available); + QVERIFY(socketDevice.read(array.data(), array.size()) == available); + + // Check that the greeting is what we expect it to be + QCOMPARE(array.constData(), "* BYE LOGOUT received\r\nXXXX OK Completed\r\n"); + + // Wait for the response + QVERIFY(socketDevice.waitForRead()); + char c; + QVERIFY(socketDevice.read(&c, sizeof(c)) == -1); + QVERIFY(socketDevice.error() == QAbstractSocket::RemoteHostClosedError); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); +} + +//---------------------------------------------------------------------------------- + +void tst_QSocks5SocketEngine::proxyAuthenticationRequired(const QNetworkProxy &, + QAuthenticator *auth) +{ + auth->setUser("qsockstest"); + auth->setPassword("password"); +} + +void tst_QSocks5SocketEngine::passwordAuth2() +{ + QSocks5SocketEngine socketDevice; + + // Initialize device + QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); + + socketDevice.setProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1081)); + socketDevice.setReceiver(this); + + QVERIFY(!socketDevice.connectToHost(QtNetworkSettings::serverIP(), 143)); + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectingState); + while (socketDevice.state() == QAbstractSocket::ConnectingState) { + QVERIFY(socketDevice.waitForWrite()); + socketDevice.connectToHost(QtNetworkSettings::serverIP(), 143); + } + if (socketDevice.state() != QAbstractSocket::ConnectedState) + qDebug("%d, %s", socketDevice.error(), socketDevice.errorString().toLatin1().constData()); + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectedState); + QVERIFY(socketDevice.peerAddress() == QtNetworkSettings::serverIP()); + + // Wait for the greeting + QVERIFY(socketDevice.waitForRead()); + + // Read the greeting + qint64 available = socketDevice.bytesAvailable(); + QVERIFY(available > 0); + QByteArray array; + array.resize(available); + QVERIFY(socketDevice.read(array.data(), array.size()) == available); + + // Check that the greeting is what we expect it to be + QVERIFY2(QtNetworkSettings::compareReplyIMAP(array), array.constData()); + + // Write a logout message + QByteArray array2 = "XXXX LOGOUT\r\n"; + QVERIFY(socketDevice.write(array2.data(), + array2.size()) == array2.size()); + + // Wait for the response + QVERIFY(socketDevice.waitForRead()); + + available = socketDevice.bytesAvailable(); + QVERIFY(available > 0); + array.resize(available); + QVERIFY(socketDevice.read(array.data(), array.size()) == available); + + // Check that the greeting is what we expect it to be + QCOMPARE(array.constData(), "* BYE LOGOUT received\r\nXXXX OK Completed\r\n"); + + // Wait for the response + QVERIFY(socketDevice.waitForRead()); + char c; + QVERIFY(socketDevice.read(&c, sizeof(c)) == -1); + QVERIFY(socketDevice.error() == QAbstractSocket::RemoteHostClosedError); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); +} + +//---------------------------------------------------------------------------------- + +QTEST_MAIN(tst_QSocks5SocketEngine) +#include "tst_qsocks5socketengine.moc" diff --git a/tests/auto/network/socket/qtcpserver/.gitignore b/tests/auto/network/socket/qtcpserver/.gitignore new file mode 100644 index 0000000000..c00e0a4ca9 --- /dev/null +++ b/tests/auto/network/socket/qtcpserver/.gitignore @@ -0,0 +1,3 @@ +tst_qtcpserver +crashingServer/crashingServer +crashingServer/crashingServer.exe diff --git a/tests/auto/network/socket/qtcpserver/crashingServer/crashingServer.pro b/tests/auto/network/socket/qtcpserver/crashingServer/crashingServer.pro new file mode 100644 index 0000000000..700e9520ec --- /dev/null +++ b/tests/auto/network/socket/qtcpserver/crashingServer/crashingServer.pro @@ -0,0 +1,9 @@ +SOURCES += main.cpp +QT = core network +CONFIG -= app_bundle +DESTDIR = ./ + +# This means the auto test works on some machines for MinGW. No dialog stalls +# the application. +win32-g++*:CONFIG += console +symbian: TARGET.CAPABILITY += NetworkServices ReadUserData diff --git a/tests/auto/network/socket/qtcpserver/crashingServer/main.cpp b/tests/auto/network/socket/qtcpserver/crashingServer/main.cpp new file mode 100644 index 0000000000..35da65f671 --- /dev/null +++ b/tests/auto/network/socket/qtcpserver/crashingServer/main.cpp @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + + QTcpServer server; + if (!server.listen(QHostAddress::LocalHost, 49199)) { + qDebug("Failed to listen: %s", server.errorString().toLatin1().constData()); + return 1; + } + +#if defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN) + QFile file(QLatin1String("/test_signal.txt")); + file.open(QIODevice::WriteOnly); + file.write("Listening\n"); + file.flush(); + file.close(); +#else + printf("Listening\n"); + fflush(stdout); +#endif + + server.waitForNewConnection(5000); + qFatal("Crash"); + return 0; +} diff --git a/tests/auto/network/socket/qtcpserver/qtcpserver.pro b/tests/auto/network/socket/qtcpserver/qtcpserver.pro new file mode 100644 index 0000000000..e123cfe73b --- /dev/null +++ b/tests/auto/network/socket/qtcpserver/qtcpserver.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs +SUBDIRS = test crashingServer + + diff --git a/tests/auto/network/socket/qtcpserver/test/test.pro b/tests/auto/network/socket/qtcpserver/test/test.pro new file mode 100644 index 0000000000..65e1d82613 --- /dev/null +++ b/tests/auto/network/socket/qtcpserver/test/test.pro @@ -0,0 +1,37 @@ +load(qttest_p4) +SOURCES += ../tst_qtcpserver.cpp + +win32: { +wince*: { + LIBS += -lws2 + crashApp.files = ../crashingServer/crashingServer.exe + crashApp.path = crashingServer + DEPLOYMENT += crashApp +} else { + LIBS += -lws2_32 +} +} + +symbian { + crashApp.files = $$QT_BUILD_TREE/examples/widgets/wiggly/$${BUILD_DIR}/crashingServer.exe + crashApp.path = . + DEPLOYMENT += crashApp + TARGET.CAPABILITY += NetworkServices ReadUserData +} + +TARGET = ../tst_qtcpserver + +win32 { + CONFIG(debug, debug|release) { + TARGET = ../../debug/tst_qtcpserver +} else { + TARGET = ../../release/tst_qtcpserver + } +} + +QT = core network + +MOC_DIR=tmp + + + diff --git a/tests/auto/network/socket/qtcpserver/tst_qtcpserver.cpp b/tests/auto/network/socket/qtcpserver/tst_qtcpserver.cpp new file mode 100644 index 0000000000..ca28a9c7c8 --- /dev/null +++ b/tests/auto/network/socket/qtcpserver/tst_qtcpserver.cpp @@ -0,0 +1,817 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Just to get Q_OS_SYMBIAN +#include +#if defined(_WIN32) && !defined(Q_OS_SYMBIAN) +#include +#else +#include +#include +#define SOCKET int +#define INVALID_SOCKET -1 +#endif + +#include + +#ifndef Q_OS_WIN +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +Q_DECLARE_METATYPE(QNetworkProxy) +Q_DECLARE_METATYPE(QList) + +#include +#include +#include +#include "../../../network-settings.h" + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QTcpServer : public QObject +{ + Q_OBJECT + +public: + tst_QTcpServer(); + virtual ~tst_QTcpServer(); + + +public slots: + void initTestCase_data(); + void initTestCase(); + void init(); + void cleanup(); +private slots: + void getSetCheck(); + void constructing(); + void clientServerLoop(); + void ipv6Server(); + void dualStack_data(); + void dualStack(); + void ipv6ServerMapped(); + void crashTests(); + void maxPendingConnections(); + void listenError(); + void waitForConnectionTest(); + void setSocketDescriptor(); + void listenWhileListening(); + void addressReusable(); + void setNewSocketDescriptorBlocking(); + void invalidProxy_data(); + void invalidProxy(); + void proxyFactory_data(); + void proxyFactory(); + + void qtbug14268_peek(); + +private: +#ifndef QT_NO_BEARERMANAGEMENT + QNetworkSession *networkSession; +#endif +}; + +// Testing get/set functions +void tst_QTcpServer::getSetCheck() +{ + QTcpServer obj1; + // int QTcpServer::maxPendingConnections() + // void QTcpServer::setMaxPendingConnections(int) + obj1.setMaxPendingConnections(0); + QCOMPARE(0, obj1.maxPendingConnections()); + obj1.setMaxPendingConnections(INT_MIN); + QCOMPARE(INT_MIN, obj1.maxPendingConnections()); + obj1.setMaxPendingConnections(INT_MAX); + QCOMPARE(INT_MAX, obj1.maxPendingConnections()); +} + +tst_QTcpServer::tst_QTcpServer() +{ + Q_SET_DEFAULT_IAP +} + +tst_QTcpServer::~tst_QTcpServer() +{ +} + +void tst_QTcpServer::initTestCase_data() +{ + QTest::addColumn("setProxy"); + QTest::addColumn("proxyType"); + + QTest::newRow("WithoutProxy") << false << 0; + QTest::newRow("WithSocks5Proxy") << true << int(QNetworkProxy::Socks5Proxy); +} + +void tst_QTcpServer::initTestCase() +{ +#ifndef QT_NO_BEARERMANAGEMENT + QNetworkConfigurationManager man; + networkSession = new QNetworkSession(man.defaultConfiguration(), this); + networkSession->open(); + QVERIFY(networkSession->waitForOpened()); +#endif +} + +void tst_QTcpServer::init() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) { + QFETCH_GLOBAL(int, proxyType); + if (proxyType == QNetworkProxy::Socks5Proxy) { + QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080)); + } + } +} + +void tst_QTcpServer::cleanup() +{ + QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy); +} + +//---------------------------------------------------------------------------------- + +void tst_QTcpServer::constructing() +{ + QTcpServer socket; + + // Check the initial state of the QTcpSocket. + QCOMPARE(socket.isListening(), false); + QCOMPARE((int)socket.serverPort(), 0); + QCOMPARE(socket.serverAddress(), QHostAddress()); + QCOMPARE(socket.maxPendingConnections(), 30); + QCOMPARE(socket.hasPendingConnections(), false); + QCOMPARE(socket.socketDescriptor(), -1); + QCOMPARE(socket.serverError(), QAbstractSocket::UnknownSocketError); + + // Check the state of the socket layer? +} + +//---------------------------------------------------------------------------------- +void tst_QTcpServer::clientServerLoop() +{ + QTcpServer server; + + QSignalSpy spy(&server, SIGNAL(newConnection())); + + QVERIFY(!server.isListening()); + QVERIFY(!server.hasPendingConnections()); + QVERIFY(server.listen(QHostAddress::Any, 11423)); + QVERIFY(server.isListening()); + + QTcpSocket client; + + QHostAddress serverAddress = QHostAddress::LocalHost; + if (!(server.serverAddress() == QHostAddress::Any) && !(server.serverAddress() == QHostAddress::AnyIPv6)) + serverAddress = server.serverAddress(); + + client.connectToHost(serverAddress, server.serverPort()); + QVERIFY(client.waitForConnected(5000)); + + QVERIFY(server.waitForNewConnection(5000)); + QVERIFY(server.hasPendingConnections()); + + QCOMPARE(spy.count(), 1); + + QTcpSocket *serverSocket = server.nextPendingConnection(); + QVERIFY(serverSocket != 0); + + QVERIFY(serverSocket->write("Greetings, client!\n", 19) == 19); + serverSocket->flush(); + + QVERIFY(client.waitForReadyRead(5000)); + QByteArray arr = client.readAll(); + QCOMPARE(arr.constData(), "Greetings, client!\n"); + + QVERIFY(client.write("Well, hello to you!\n", 20) == 20); + client.flush(); + + QVERIFY(serverSocket->waitForReadyRead(5000)); + arr = serverSocket->readAll(); + QCOMPARE(arr.constData(), "Well, hello to you!\n"); +} + +//---------------------------------------------------------------------------------- +void tst_QTcpServer::ipv6Server() +{ + //### need to enter the event loop for the server to get the connection ?? ( windows) + QTcpServer server; + if (!server.listen(QHostAddress::LocalHostIPv6, 8944)) { + QVERIFY(server.serverError() == QAbstractSocket::UnsupportedSocketOperationError); + return; + } + + QVERIFY(server.serverPort() == 8944); + QVERIFY(server.serverAddress() == QHostAddress::LocalHostIPv6); + + QTcpSocket client; + client.connectToHost("::1", 8944); + QVERIFY(client.waitForConnected(5000)); + + QVERIFY(server.waitForNewConnection()); + QVERIFY(server.hasPendingConnections()); + + QTcpSocket *serverSocket = 0; + QVERIFY((serverSocket = server.nextPendingConnection())); + serverSocket->close(); + delete serverSocket; +} + +Q_DECLARE_METATYPE(QHostAddress); + +void tst_QTcpServer::dualStack_data() +{ + QTest::addColumn("bindAddress"); + QTest::addColumn("v4ok"); + QTest::addColumn("v6ok"); + QTest::newRow("any") << QHostAddress(QHostAddress::Any) << true << true; + QTest::newRow("anyIPv4") << QHostAddress(QHostAddress::AnyIPv4) << true << false; + QTest::newRow("anyIPv6") << QHostAddress(QHostAddress::AnyIPv6) << false << true; +} + +void tst_QTcpServer::dualStack() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + QSKIP("test server proxy doesn't support ipv6", SkipSingle); + QFETCH(QHostAddress, bindAddress); + QFETCH(bool, v4ok); + QFETCH(bool, v6ok); + + QTcpServer server; + QVERIFY(server.listen(bindAddress)); + + QTcpSocket v4client; + v4client.connectToHost(QHostAddress::LocalHost, server.serverPort()); + + QTcpSocket v6client; + v6client.connectToHost(QHostAddress::LocalHostIPv6, server.serverPort()); + + QCOMPARE(v4client.waitForConnected(5000), v4ok); + QCOMPARE(v6client.waitForConnected(5000), v6ok); +} + +//---------------------------------------------------------------------------------- +void tst_QTcpServer::ipv6ServerMapped() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + QTcpServer server; + QVERIFY(server.listen(QHostAddress::LocalHost)); + + // let's try the normal case + QTcpSocket client1; + client1.connectToHost("127.0.0.1", server.serverPort()); + QVERIFY(server.waitForNewConnection(5000)); + delete server.nextPendingConnection(); + + // let's try the mapped one in the nice format + QTcpSocket client2; + client2.connectToHost("::ffff:127.0.0.1", server.serverPort()); + QVERIFY(server.waitForNewConnection(5000)); + delete server.nextPendingConnection(); + + // let's try the mapped in hex format + QTcpSocket client3; + client3.connectToHost("::ffff:7F00:0001", server.serverPort()); + QVERIFY(server.waitForNewConnection(5000)); + delete server.nextPendingConnection(); + + // However connecting to the v6 localhost should not work + QTcpSocket client4; + client4.connectToHost("::1", server.serverPort()); + QVERIFY(!server.waitForNewConnection(5000)); +} + +//---------------------------------------------------------------------------------- +void tst_QTcpServer::crashTests() +{ + QTcpServer server; + server.close(); + QVERIFY(server.listen()); +} + +//---------------------------------------------------------------------------------- +void tst_QTcpServer::maxPendingConnections() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) { + QFETCH_GLOBAL(int, proxyType); + if (proxyType == QNetworkProxy::Socks5Proxy) { + QSKIP("With socks5 only 1 connection is allowed ever", SkipAll); + } + } + //### sees to fail sometimes ... a timing issue with the test on windows + QTcpServer server; + server.setMaxPendingConnections(2); + + QTcpSocket socket1; + QTcpSocket socket2; + QTcpSocket socket3; + + QVERIFY(server.listen()); + + socket1.connectToHost(QHostAddress::LocalHost, server.serverPort()); + socket2.connectToHost(QHostAddress::LocalHost, server.serverPort()); + socket3.connectToHost(QHostAddress::LocalHost, server.serverPort()); + + QVERIFY(server.waitForNewConnection(5000)); + + QVERIFY(server.hasPendingConnections()); + QVERIFY(server.nextPendingConnection()); + QVERIFY(server.hasPendingConnections()); + QVERIFY(server.nextPendingConnection()); + QVERIFY(!server.hasPendingConnections()); + QCOMPARE(server.nextPendingConnection(), (QTcpSocket*)0); + + QVERIFY(server.waitForNewConnection(5000)); + + QVERIFY(server.hasPendingConnections()); + QVERIFY(server.nextPendingConnection()); +} + +//---------------------------------------------------------------------------------- +void tst_QTcpServer::listenError() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) { + QFETCH_GLOBAL(int, proxyType); + if (proxyType == QNetworkProxy::Socks5Proxy) { + QSKIP("With socks5 we can not make hard requirements on the address or port", SkipAll); + } + } + QTcpServer server; + QVERIFY(!server.listen(QHostAddress("1.2.3.4"), 0)); + QCOMPARE(server.serverError(), QAbstractSocket::SocketAddressNotAvailableError); + QCOMPARE(server.errorString().toLatin1().constData(), "The address is not available"); +} + +class ThreadConnector : public QThread +{ +public: + ThreadConnector(const QHostAddress &host, quint16 port) + : host(host), port(port) + { } + + ~ThreadConnector() + { + wait(); + } + +protected: + void run() + { + sleep(2); + + QTcpSocket socket; + socket.connectToHost(host, port); + + QEventLoop loop; + QTimer::singleShot(5000, &loop, SLOT(quit())); + loop.exec(); + } + +private: + QHostAddress host; + quint16 port; +}; + +//---------------------------------------------------------------------------------- +void tst_QTcpServer::waitForConnectionTest() +{ + + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) { + QFETCH_GLOBAL(int, proxyType); + if (proxyType == QNetworkProxy::Socks5Proxy) { + QSKIP("Localhost servers don't work well with SOCKS5", SkipAll); + } + } + + QTcpSocket findLocalIpSocket; + findLocalIpSocket.connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(findLocalIpSocket.waitForConnected(5000)); + + QTcpServer server; + bool timeout = false; + QVERIFY(server.listen(findLocalIpSocket.localAddress())); + QVERIFY(!server.waitForNewConnection(1000, &timeout)); + QCOMPARE(server.serverError(), QAbstractSocket::SocketTimeoutError); + QVERIFY(timeout); + + ThreadConnector connector(findLocalIpSocket.localAddress(), server.serverPort()); + connector.start(); + +#if defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN) + QVERIFY(server.waitForNewConnection(9000, &timeout)); +#else + QVERIFY(server.waitForNewConnection(3000, &timeout)); +#endif + QVERIFY(!timeout); +} + +//---------------------------------------------------------------------------------- +void tst_QTcpServer::setSocketDescriptor() +{ + QTcpServer server; +#ifdef Q_OS_SYMBIAN + QTest::ignoreMessage(QtWarningMsg, "QSymbianSocketEngine::initialize - socket descriptor not found"); +#endif + QVERIFY(!server.setSocketDescriptor(42)); + QCOMPARE(server.serverError(), QAbstractSocket::UnsupportedSocketOperationError); +#ifndef Q_OS_SYMBIAN + //adopting Open C sockets is not supported, neither is adopting externally created RSocket +#ifdef Q_OS_WIN + // ensure winsock is started + WSADATA wsaData; + QVERIFY(WSAStartup(MAKEWORD(2,0), &wsaData) == NO_ERROR); +#endif + + SOCKET sock = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + QVERIFY(sock != INVALID_SOCKET); + + sockaddr_in sin; + memset(&sin, 0, sizeof(sockaddr_in)); + sin.sin_family = AF_INET; + sin.sin_port = 0; + sin.sin_addr.s_addr = 0x00000000; + QVERIFY(::bind(sock, (sockaddr*)&sin, sizeof(sockaddr_in)) == 0); + QVERIFY(::listen(sock, 10) == 0); + QVERIFY(server.setSocketDescriptor(sock)); + +#ifdef Q_OS_WIN + WSACleanup(); +#endif +#endif +} + +//---------------------------------------------------------------------------------- +void tst_QTcpServer::listenWhileListening() +{ + QTcpServer server; + QVERIFY(server.listen()); + QTest::ignoreMessage(QtWarningMsg, "QTcpServer::listen() called when already listening"); + QVERIFY(!server.listen()); +} + +//---------------------------------------------------------------------------------- + +class SeverWithBlockingSockets : public QTcpServer +{ +public: + SeverWithBlockingSockets() + : ok(false) { } + + bool ok; + +protected: + void incomingConnection(int socketDescriptor) + { + // how a user woulddo it (qabstractsocketengine is not public) + unsigned long arg = 0; +#if defined(Q_OS_SYMBIAN) + arg = fcntl(socketDescriptor, F_GETFL, NULL); + arg &= (~O_NONBLOCK); + ok = ::fcntl(socketDescriptor, F_SETFL, arg) != -1; +#elif defined(Q_OS_WIN) + ok = ::ioctlsocket(socketDescriptor, FIONBIO, &arg) == 0; + ::closesocket(socketDescriptor); +#else + ok = ::ioctl(socketDescriptor, FIONBIO, &arg) == 0; + ::close(socketDescriptor); +#endif + } +}; + +void tst_QTcpServer::addressReusable() +{ +#if defined(Q_OS_SYMBIAN) && defined(Q_CC_NOKIAX86) + QSKIP("Symbian: Emulator does not support process launching", SkipAll ); +#endif + +#if defined(QT_NO_PROCESS) + QSKIP("Qt was compiled with QT_NO_PROCESS", SkipAll); +#else + + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) { + QFETCH_GLOBAL(int, proxyType); + if (proxyType == QNetworkProxy::Socks5Proxy) { + QSKIP("With socks5 this test does not make senans at the momment", SkipAll); + } + } +#if defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN) + QString signalName = QString::fromLatin1("/test_signal.txt"); + QFile::remove(signalName); + // The crashingServer process will crash once it gets a connection. + QProcess process; + process.start("crashingServer/crashingServer"); + int waitCount = 5; + while (waitCount-- && !QFile::exists(signalName)) + QTest::qWait(1000); + QVERIFY(QFile::exists(signalName)); + QFile::remove(signalName); +#else + // The crashingServer process will crash once it gets a connection. + QProcess process; + process.start("crashingServer/crashingServer"); + QVERIFY(process.waitForReadyRead(5000)); +#endif + + QTcpSocket socket; + socket.connectToHost(QHostAddress::LocalHost, 49199); + QVERIFY(socket.waitForConnected(5000)); + + QVERIFY(process.waitForFinished(30000)); + + // Give the system some time. + QTest::qSleep(10); + + QTcpServer server; + QVERIFY(server.listen(QHostAddress::LocalHost, 49199)); +#endif +} + +void tst_QTcpServer::setNewSocketDescriptorBlocking() +{ +#ifdef Q_OS_SYMBIAN + QSKIP("open C ioctls on Qt sockets not supported", SkipAll); +#else + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) { + QFETCH_GLOBAL(int, proxyType); + if (proxyType == QNetworkProxy::Socks5Proxy) { + QSKIP("With socks5 we can not make the socket descripter blocking", SkipAll); + } + } + SeverWithBlockingSockets server; + QVERIFY(server.listen()); + + QTcpSocket socket; + socket.connectToHost(QHostAddress::LocalHost, server.serverPort()); + QVERIFY(server.waitForNewConnection(5000)); + QVERIFY(server.ok); +#endif +} + +void tst_QTcpServer::invalidProxy_data() +{ + QTest::addColumn("type"); + QTest::addColumn("host"); + QTest::addColumn("port"); + QTest::addColumn("expectedError"); + + QString fluke = QHostInfo::fromName(QtNetworkSettings::serverName()).addresses().first().toString(); + QTest::newRow("ftp-proxy") << int(QNetworkProxy::FtpCachingProxy) << fluke << 143 + << int(QAbstractSocket::UnsupportedSocketOperationError); + QTest::newRow("http-proxy") << int(QNetworkProxy::HttpProxy) << fluke << 3128 + << int(QAbstractSocket::UnsupportedSocketOperationError); + + QTest::newRow("no-such-host") << int(QNetworkProxy::Socks5Proxy) + << "this-host-will-never-exist.troll.no" << 1080 + << int(QAbstractSocket::ProxyNotFoundError); + QTest::newRow("socks5-on-http") << int(QNetworkProxy::Socks5Proxy) << fluke << 3128 + << int(QAbstractSocket::SocketTimeoutError); +} + +void tst_QTcpServer::invalidProxy() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + QFETCH(int, type); + QFETCH(QString, host); + QFETCH(int, port); + QNetworkProxy::ProxyType proxyType = QNetworkProxy::ProxyType(type); + QNetworkProxy proxy(proxyType, host, port); + + QTcpServer server; + server.setProxy(proxy); + bool listenResult = server.listen(); + + QVERIFY(!listenResult); + QVERIFY(!server.errorString().isEmpty()); + + // note: the following test is not a hard failure. + // Sometimes, error codes change for the better + QTEST(int(server.serverError()), "expectedError"); +} + +// copied from tst_qnetworkreply.cpp +class MyProxyFactory: public QNetworkProxyFactory +{ +public: + int callCount; + QList toReturn; + QNetworkProxyQuery lastQuery; + inline MyProxyFactory() { clear(); } + + inline void clear() + { + callCount = 0; + toReturn = QList() << QNetworkProxy::DefaultProxy; + lastQuery = QNetworkProxyQuery(); + } + + virtual QList queryProxy(const QNetworkProxyQuery &query) + { + lastQuery = query; + ++callCount; + return toReturn; + } +}; + +void tst_QTcpServer::proxyFactory_data() +{ + QTest::addColumn >("proxyList"); + QTest::addColumn("proxyUsed"); + QTest::addColumn("fails"); + QTest::addColumn("expectedError"); + + QList proxyList; + + // tests that do get to listen + + proxyList << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080); + QTest::newRow("socks5") + << proxyList << proxyList.at(0) + << false << int(QAbstractSocket::UnknownSocketError); + + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3128) + << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080); + QTest::newRow("cachinghttp+socks5") + << proxyList << proxyList.at(1) + << false << int(QAbstractSocket::UnknownSocketError); + + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121) + << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3128) + << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080); + QTest::newRow("ftp+cachinghttp+socks5") + << proxyList << proxyList.at(2) + << false << int(QAbstractSocket::UnknownSocketError); + + // tests that fail to listen + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3128); + QTest::newRow("http") + << proxyList << proxyList.at(0) + << true << int(QAbstractSocket::UnsupportedSocketOperationError); + + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3128); + QTest::newRow("cachinghttp") + << proxyList << QNetworkProxy() + << true << int(QAbstractSocket::UnsupportedSocketOperationError); + + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121); + QTest::newRow("ftp") + << proxyList << QNetworkProxy() + << true << int(QAbstractSocket::UnsupportedSocketOperationError); + + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121) + << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3128); + QTest::newRow("ftp+cachinghttp") + << proxyList << QNetworkProxy() + << true << int(QAbstractSocket::UnsupportedSocketOperationError); +} + +void tst_QTcpServer::proxyFactory() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + QFETCH(QList, proxyList); + QFETCH(QNetworkProxy, proxyUsed); + QFETCH(bool, fails); + + MyProxyFactory *factory = new MyProxyFactory; + factory->toReturn = proxyList; + QNetworkProxyFactory::setApplicationProxyFactory(factory); + + QTcpServer server; + bool listenResult = server.listen(); + + // Verify that the factory was called properly + QCOMPARE(factory->callCount, 1); + QCOMPARE(factory->lastQuery, QNetworkProxyQuery(0, QString(), QNetworkProxyQuery::TcpServer)); + + QCOMPARE(listenResult, !fails); + QCOMPARE(server.errorString().isEmpty(), !fails); + + // note: the following test is not a hard failure. + // Sometimes, error codes change for the better + QTEST(int(server.serverError()), "expectedError"); +} + +class Qtbug14268Helper : public QObject +{ + Q_OBJECT +public: + QByteArray lastDataPeeked; +public slots: + void newConnection() { + QTcpServer* server=static_cast(sender()); + QTcpSocket* s=server->nextPendingConnection(); + connect(s,SIGNAL(readyRead()),this,SLOT(onServerReadyRead())); + } + void onServerReadyRead() { + QTcpSocket* clientSocket=static_cast(sender()); + lastDataPeeked = clientSocket->peek(128*1024).toHex(); + QTestEventLoop::instance().exitLoop(); + } +}; + +// there is a similar test inside tst_qtcpsocket that uses the waitFor* functions instead +void tst_QTcpServer::qtbug14268_peek() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + QTcpServer server; + server.listen(); + + Qtbug14268Helper helper; + QObject::connect(&server, SIGNAL(newConnection()), &helper, SLOT(newConnection())); + + QTcpSocket client; + client.connectToHost(QHostAddress::LocalHost, server.serverPort()); + QVERIFY(client.waitForConnected(2000)); + + client.write("abc\n"); + QTestEventLoop::instance().enterLoop(5); + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(helper.lastDataPeeked == QByteArray("6162630a")); + + client.write("def\n"); + QTestEventLoop::instance().enterLoop(5); + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(helper.lastDataPeeked == QByteArray("6162630a6465660a")); + + client.write("ghi\n"); + QTestEventLoop::instance().enterLoop(5); + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(helper.lastDataPeeked == QByteArray("6162630a6465660a6768690a")); +} + +QTEST_MAIN(tst_QTcpServer) +#include "tst_qtcpserver.moc" diff --git a/tests/auto/network/socket/qtcpsocket/.gitignore b/tests/auto/network/socket/qtcpsocket/.gitignore new file mode 100644 index 0000000000..d456ab15d0 --- /dev/null +++ b/tests/auto/network/socket/qtcpsocket/.gitignore @@ -0,0 +1,3 @@ +tst_qtcpsocket +stressTest/stressTest +crashingServer/crashingServer diff --git a/tests/auto/network/socket/qtcpsocket/qtcpsocket.pro b/tests/auto/network/socket/qtcpsocket/qtcpsocket.pro new file mode 100644 index 0000000000..5dfff5bb88 --- /dev/null +++ b/tests/auto/network/socket/qtcpsocket/qtcpsocket.pro @@ -0,0 +1,8 @@ +TEMPLATE = subdirs + + +!wince*: SUBDIRS = test stressTest +wince*|symbian|vxworks* : SUBDIRS = test + + +requires(contains(QT_CONFIG,private_tests)) diff --git a/tests/auto/network/socket/qtcpsocket/stressTest/Test.cpp b/tests/auto/network/socket/qtcpsocket/stressTest/Test.cpp new file mode 100644 index 0000000000..995fc0528c --- /dev/null +++ b/tests/auto/network/socket/qtcpsocket/stressTest/Test.cpp @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +// Qt +#include +#include +#include +#include + +// Test +#include "Test.h" + +//------------------------------------------------------------------------------ +My4Socket::My4Socket(QObject *parent) + : QTcpSocket(parent), safeShutDown(false) +{ + connect(this, SIGNAL(readyRead()), this, SLOT(read())); + connect(this, SIGNAL(disconnected()), this, SLOT(closed())); +} + +//------------------------------------------------------------------------------ +void My4Socket::read(void) +{ + QDataStream in(this); + + quint32 num, reply; + + while (bytesAvailable()) { + in >> num; + if (num == 42) { + safeShutDown = true; + qDebug("SUCCESS"); + QCoreApplication::instance()->quit(); + return; + } + reply = num + 1; + if (reply == 42) + ++reply; + } + + // Reply with a bigger number + sendTest(reply); +} + +//------------------------------------------------------------------------------ +void My4Socket::closed(void) +{ + if (!safeShutDown) + qDebug("FAILED"); + QCoreApplication::instance()->quit(); +} + +//------------------------------------------------------------------------------ +void My4Socket::sendTest(quint32 num) +{ + QByteArray block; + QDataStream out(&block, QIODevice::WriteOnly); + out << num; + + write(block, block.size()); +} + +//------------------------------------------------------------------------------ +My4Server::My4Server(QObject *parent) + : QTcpServer(parent) +{ + if (listen(QHostAddress::Any, 7700)) + qDebug("qt4server"); + QTimer::singleShot(5000, this, SLOT(stopServer())); +} + +//------------------------------------------------------------------------------ +void My4Server::incomingConnection(int socketId) +{ + m_socket = new My4Socket(this); + m_socket->setSocketDescriptor(socketId); +} + +//------------------------------------------------------------------------------ +void My4Server::stopServer() +{ + if (m_socket) { + qDebug("SUCCESS"); + m_socket->safeShutDown = true; + m_socket->sendTest(42); + } else { + QCoreApplication::instance()->quit(); + } +} + +//------------------------------------------------------------------------------ +Test::Test(Type type) +{ + switch (type) { + case Qt4Client: { + qDebug("qt4client"); + My4Socket *s = new My4Socket(this); + s->connectToHost("localhost", 7700); + s->sendTest(1); + break; + } + case Qt4Server: { + new My4Server(this); + break; + } + default: + break; + } +} diff --git a/tests/auto/network/socket/qtcpsocket/stressTest/Test.h b/tests/auto/network/socket/qtcpsocket/stressTest/Test.h new file mode 100644 index 0000000000..5440e7ffa9 --- /dev/null +++ b/tests/auto/network/socket/qtcpsocket/stressTest/Test.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef TEST_H +#define TEST_H + +//------------------------------------------------------------------------------ + +#include +#include + +//------------------------------------------------------------------------------ +class My4Socket : public QTcpSocket +{ + Q_OBJECT +public: + My4Socket(QObject *parent); + + void sendTest(quint32 num); + bool safeShutDown; + +private slots: + void read(); + void closed(); +}; + +//------------------------------------------------------------------------------ +class My4Server : public QTcpServer +{ + Q_OBJECT +public: + My4Server(QObject *parent = 0); + +protected: + void incomingConnection(int socket); + +private slots: + void stopServer(); + +private: + My4Socket *m_socket; +}; + +//------------------------------------------------------------------------------ +class Test : public QObject +{ + Q_OBJECT + +public: + enum Type { + Qt4Client, + Qt4Server, + }; + Test(Type type); +}; + +//------------------------------------------------------------------------------ +#endif // TEST_H diff --git a/tests/auto/network/socket/qtcpsocket/stressTest/main.cpp b/tests/auto/network/socket/qtcpsocket/stressTest/main.cpp new file mode 100644 index 0000000000..76ce7bd820 --- /dev/null +++ b/tests/auto/network/socket/qtcpsocket/stressTest/main.cpp @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "Test.h" + +#include +#include + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + + QString arg; + if (app.arguments().size() > 1) + arg = app.arguments().at(1).toLower().trimmed(); + + Test::Type type; + if (arg == QLatin1String("qt4client")) + type = Test::Qt4Client; + else if (arg == QLatin1String("qt4server")) + type = Test::Qt4Server; + else { + qDebug("usage: ./stressTest "); + return 0; + } + + Test test(type); + + return app.exec(); +} diff --git a/tests/auto/network/socket/qtcpsocket/stressTest/stressTest.pro b/tests/auto/network/socket/qtcpsocket/stressTest/stressTest.pro new file mode 100644 index 0000000000..f6215f80a0 --- /dev/null +++ b/tests/auto/network/socket/qtcpsocket/stressTest/stressTest.pro @@ -0,0 +1,12 @@ +HEADERS += Test.h +SOURCES += main.cpp Test.cpp +QT += network + +CONFIG -= app_bundle +CONFIG += console +DESTDIR = ./ +MOC_DIR = .moc/ +TMP_DIR = .tmp/ + +symbian: TARGET.CAPABILITY = NetworkServices + diff --git a/tests/auto/network/socket/qtcpsocket/test/test.pro b/tests/auto/network/socket/qtcpsocket/test/test.pro new file mode 100644 index 0000000000..61bfaad1a1 --- /dev/null +++ b/tests/auto/network/socket/qtcpsocket/test/test.pro @@ -0,0 +1,33 @@ +load(qttest_p4) + +QT += widgets +QT += core-private network-private +SOURCES += ../tst_qtcpsocket.cpp +win32: { +wince*: { + LIBS += -lws2 +} else { + LIBS += -lws2_32 +} +} +QT += network +vxworks:QT -= gui + +symbian: { + TARGET.EPOCHEAPSIZE="0x100 0x3000000" + TARGET.CAPABILITY = NetworkServices ReadUserData +} + +TARGET = tst_qtcpsocket + +win32 { + CONFIG(debug, debug|release) { + DESTDIR = ../debug +} else { + DESTDIR = ../release + } +} else { + DESTDIR = ../ +} + +CONFIG+=insignificant_test # unstable, QTBUG-21043 diff --git a/tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp b/tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp new file mode 100644 index 0000000000..bae3f7c2f5 --- /dev/null +++ b/tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp @@ -0,0 +1,2684 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +// Just to get Q_OS_SYMBIAN +#include + +#if defined(_WIN32) && !defined(Q_OS_SYMBIAN) +#include +#else +#include +#include +#include +#include +#define SOCKET int +#define INVALID_SOCKET -1 +#endif + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#ifndef Q_OS_VXWORKS +#include +#include +#endif +#include +#include +#include +#include +#include +#ifndef QT_NO_OPENSSL +#include +#endif +#include +#include +#include +#include +#include +// RVCT compiles also unused inline methods +# include + +#ifdef Q_OS_LINUX +#include +#include +#include +#include +#endif + +#include "private/qhostinfo_p.h" + +#include "../../../network-settings.h" +#include "../../../../shared/util.h" + +Q_DECLARE_METATYPE(QAbstractSocket::SocketError) +Q_DECLARE_METATYPE(QAbstractSocket::SocketState) +Q_DECLARE_METATYPE(QNetworkProxy) +Q_DECLARE_METATYPE(QList) + +//TESTED_CLASS= +//TESTED_FILES= + +QT_FORWARD_DECLARE_CLASS(QTcpSocket) +QT_FORWARD_DECLARE_CLASS(SocketPair) + +class tst_QTcpSocket : public QObject +{ + Q_OBJECT + +public: + tst_QTcpSocket(); + virtual ~tst_QTcpSocket(); + + static void enterLoop(int secs) + { + ++loopLevel; + QTestEventLoop::instance().enterLoop(secs); + --loopLevel; + } + static void exitLoop() + { + // Safe exit - if we aren't in an event loop, don't + // exit one. + if (loopLevel > 0) + QTestEventLoop::instance().exitLoop(); + } + static bool timeout() + { + return QTestEventLoop::instance().timeout(); + } + +public slots: + void initTestCase_data(); + void init(); + void cleanup(); +private slots: + void socketsConstructedBeforeEventLoop(); + void constructing(); + void setInvalidSocketDescriptor(); + void setSocketDescriptor(); + void socketDescriptor(); + void blockingIMAP(); + void nonBlockingIMAP(); + void hostNotFound(); + void timeoutConnect_data(); + void timeoutConnect(); + void delayedClose(); + void partialRead(); + void unget(); + void readAllAfterClose(); + void openCloseOpenClose(); + void connectDisconnectConnectDisconnect(); + void disconnectWhileConnecting_data(); + void disconnectWhileConnecting(); + void disconnectWhileConnectingNoEventLoop_data(); + void disconnectWhileConnectingNoEventLoop(); + void disconnectWhileLookingUp_data(); + void disconnectWhileLookingUp(); + void downloadBigFile(); + void readLine(); + void readLineString(); + void readChunks(); + void waitForBytesWritten(); + void waitForBytesWrittenMinusOne(); + void waitForReadyRead(); + void waitForReadyReadMinusOne(); + void flush(); + void synchronousApi(); + void dontCloseOnTimeout(); + void recursiveReadyRead(); + void atEnd(); + void socketInAThread(); + void socketsInThreads(); + void waitForReadyReadInASlot(); + void remoteCloseError(); + void openMessageBoxInErrorSlot(); +#ifndef Q_OS_WIN + void connectToLocalHostNoService(); +#endif + void waitForConnectedInHostLookupSlot(); + void waitForConnectedInHostLookupSlot2(); + void readyReadSignalsAfterWaitForReadyRead(); +#ifdef Q_OS_LINUX + void linuxKernelBugLocalSocket(); +#endif + void abortiveClose(); + void localAddressEmptyOnBSD(); + void zeroAndMinusOneReturns(); + void connectionRefused(); + void suddenRemoteDisconnect_data(); + void suddenRemoteDisconnect(); + void connectToMultiIP(); + void moveToThread0(); + void increaseReadBufferSize(); + void taskQtBug5799ConnectionErrorWaitForConnected(); + void taskQtBug5799ConnectionErrorEventLoop(); + void taskQtBug7054TimeoutErrorResetting(); + + void invalidProxy_data(); + void invalidProxy(); + void proxyFactory_data(); + void proxyFactory(); + + void qtbug14268_peek(); + + +protected slots: + void nonBlockingIMAP_hostFound(); + void nonBlockingIMAP_connected(); + void nonBlockingIMAP_closed(); + void nonBlockingIMAP_readyRead(); + void nonBlockingIMAP_bytesWritten(qint64); + void readRegularFile_readyRead(); + void exitLoopSlot(); + void downloadBigFileSlot(); + void recursiveReadyReadSlot(); + void waitForReadyReadInASlotSlot(); + void messageBoxSlot(); + void hostLookupSlot(); + void abortiveClose_abortSlot(); + void remoteCloseErrorSlot(); + void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth); + void earlySocketBytesSent(qint64 bytes); + void earlySocketReadyRead(); + +private: + QByteArray expectedReplyIMAP(); + void fetchExpectedReplyIMAP(); + QTcpSocket *newSocket() const; + QTcpSocket *nonBlockingIMAP_socket; + QStringList nonBlockingIMAP_data; + qint64 nonBlockingIMAP_totalWritten; + + QTcpSocket *tmpSocket; + qint64 bytesAvailable; + qint64 expectedLength; + bool readingBody; + QTime timer; + + QByteArray expectedReplyIMAP_cached; + + mutable int proxyAuthCalled; + + bool gotClosedSignal; + int numConnections; + static int loopLevel; + + SocketPair *earlyConstructedSockets; + int earlyBytesWrittenCount; + int earlyReadyReadCount; +}; + +enum ProxyTests { + NoProxy = 0x00, + Socks5Proxy = 0x01, + HttpProxy = 0x02, + TypeMask = 0x0f, + + NoAuth = 0x00, + AuthBasic = 0x10, + AuthNtlm = 0x20, + AuthMask = 0xf0 +}; + +int tst_QTcpSocket::loopLevel = 0; + +class SocketPair: public QObject +{ + Q_OBJECT +public: + QTcpSocket *endPoints[2]; + + SocketPair(QObject *parent = 0) + : QObject(parent) + { + endPoints[0] = endPoints[1] = 0; + } + + bool create() + { + QTcpServer server; + server.listen(); + + QTcpSocket *active = new QTcpSocket(this); + active->connectToHost("127.0.0.1", server.serverPort()); + + if (!active->waitForConnected(1000)) + return false; + + if (!server.waitForNewConnection(1000)) + return false; + + QTcpSocket *passive = server.nextPendingConnection(); + passive->setParent(this); + + endPoints[0] = active; + endPoints[1] = passive; + return true; + } +}; + +tst_QTcpSocket::tst_QTcpSocket() +{ + tmpSocket = 0; + + //This code relates to the socketsConstructedBeforeEventLoop test case + earlyConstructedSockets = new SocketPair; + QVERIFY(earlyConstructedSockets->create()); + earlyBytesWrittenCount = 0; + earlyReadyReadCount = 0; + connect(earlyConstructedSockets->endPoints[0], SIGNAL(readyRead()), this, SLOT(earlySocketReadyRead())); + connect(earlyConstructedSockets->endPoints[1], SIGNAL(bytesWritten(qint64)), this, SLOT(earlySocketBytesSent(qint64))); + earlyConstructedSockets->endPoints[1]->write("hello work"); +} + +tst_QTcpSocket::~tst_QTcpSocket() +{ + +} + +void tst_QTcpSocket::initTestCase_data() +{ + QTest::addColumn("setProxy"); + QTest::addColumn("proxyType"); + QTest::addColumn("ssl"); + + qDebug() << QtNetworkSettings::serverName(); + QTest::newRow("WithoutProxy") << false << 0 << false; + QTest::newRow("WithSocks5Proxy") << true << int(Socks5Proxy) << false; + QTest::newRow("WithSocks5ProxyAuth") << true << int(Socks5Proxy | AuthBasic) << false; + + QTest::newRow("WithHttpProxy") << true << int(HttpProxy) << false; + QTest::newRow("WithHttpProxyBasicAuth") << true << int(HttpProxy | AuthBasic) << false; +// QTest::newRow("WithHttpProxyNtlmAuth") << true << int(HttpProxy | AuthNtlm) << false; + +#ifndef QT_NO_OPENSSL + QTest::newRow("WithoutProxy SSL") << false << 0 << true; + QTest::newRow("WithSocks5Proxy SSL") << true << int(Socks5Proxy) << true; + QTest::newRow("WithSocks5AuthProxy SSL") << true << int(Socks5Proxy | AuthBasic) << true; + + QTest::newRow("WithHttpProxy SSL") << true << int(HttpProxy) << true; + QTest::newRow("WithHttpProxyBasicAuth SSL") << true << int(HttpProxy | AuthBasic) << true; +// QTest::newRow("WithHttpProxyNtlmAuth SSL") << true << int(HttpProxy | AuthNtlm) << true; +#endif +} + +void tst_QTcpSocket::init() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) { + QFETCH_GLOBAL(int, proxyType); + QList addresses = QHostInfo::fromName(QtNetworkSettings::serverName()).addresses(); + QVERIFY2(addresses.count() > 0, "failed to get ip address for test server"); + QString fluke = addresses.first().toString(); + QNetworkProxy proxy; + + switch (proxyType) { + case Socks5Proxy: + proxy = QNetworkProxy(QNetworkProxy::Socks5Proxy, fluke, 1080); + break; + + case Socks5Proxy | AuthBasic: + proxy = QNetworkProxy(QNetworkProxy::Socks5Proxy, fluke, 1081); + break; + + case HttpProxy | NoAuth: + proxy = QNetworkProxy(QNetworkProxy::HttpProxy, fluke, 3128); + break; + + case HttpProxy | AuthBasic: + proxy = QNetworkProxy(QNetworkProxy::HttpProxy, fluke, 3129); + break; + + case HttpProxy | AuthNtlm: + proxy = QNetworkProxy(QNetworkProxy::HttpProxy, fluke, 3130); + break; + } + QNetworkProxy::setApplicationProxy(proxy); + } + + qt_qhostinfo_clear_cache(); +} + +QTcpSocket *tst_QTcpSocket::newSocket() const +{ + QTcpSocket *socket; +#ifndef QT_NO_OPENSSL + QFETCH_GLOBAL(bool, ssl); + socket = ssl ? new QSslSocket : new QTcpSocket; +#else + socket = new QTcpSocket; +#endif + + proxyAuthCalled = 0; + connect(socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + Qt::DirectConnection); + return socket; +} + +void tst_QTcpSocket::cleanup() +{ + QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy); +} + +void tst_QTcpSocket::proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth) +{ + ++proxyAuthCalled; + auth->setUser("qsockstest"); + auth->setPassword("password"); +} + +//---------------------------------------------------------------------------------- + +void tst_QTcpSocket::socketsConstructedBeforeEventLoop() +{ + QFETCH_GLOBAL(bool, setProxy); + QFETCH_GLOBAL(bool, ssl); + if (setProxy || ssl) + return; + //This test checks that sockets constructed before QCoreApplication::exec() still emit signals + //see construction code in the tst_QTcpSocket constructor + enterLoop(3); + QCOMPARE(earlyBytesWrittenCount, 1); + QCOMPARE(earlyReadyReadCount, 1); + earlyConstructedSockets->endPoints[0]->close(); + earlyConstructedSockets->endPoints[1]->close(); +} + +void tst_QTcpSocket::earlySocketBytesSent(qint64 /* bytes */) +{ + earlyBytesWrittenCount++; +} + +void tst_QTcpSocket::earlySocketReadyRead() +{ + earlyReadyReadCount++; +} + +//---------------------------------------------------------------------------------- + +void tst_QTcpSocket::constructing() +{ + QTcpSocket *socket = newSocket(); + + // Check the initial state of the QTcpSocket. + QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); + QVERIFY(socket->isSequential()); + QVERIFY(!socket->isOpen()); + QVERIFY(!socket->isValid()); + QCOMPARE(socket->socketType(), QTcpSocket::TcpSocket); + + char c; + QCOMPARE(socket->getChar(&c), false); + QCOMPARE((int) socket->bytesAvailable(), 0); + QCOMPARE(socket->canReadLine(), false); + QCOMPARE(socket->readLine(), QByteArray()); + QCOMPARE(socket->socketDescriptor(), -1); + QCOMPARE((int) socket->localPort(), 0); + QVERIFY(socket->localAddress() == QHostAddress()); + QCOMPARE((int) socket->peerPort(), 0); + QVERIFY(socket->peerAddress() == QHostAddress()); + QCOMPARE(socket->error(), QTcpSocket::UnknownSocketError); + QCOMPARE(socket->errorString(), QString("Unknown error")); + + // Check the state of the socket layer? + delete socket; +} + +//---------------------------------------------------------------------------------- + +void tst_QTcpSocket::setInvalidSocketDescriptor() +{ + QTcpSocket *socket = newSocket(); + QCOMPARE(socket->socketDescriptor(), -1); +#ifdef Q_OS_SYMBIAN + QTest::ignoreMessage(QtWarningMsg, "QSymbianSocketEngine::initialize - socket descriptor not found"); +#endif + QVERIFY(!socket->setSocketDescriptor(-5, QTcpSocket::UnconnectedState)); + QCOMPARE(socket->socketDescriptor(), -1); + + QCOMPARE(socket->error(), QTcpSocket::UnsupportedSocketOperationError); + + delete socket; +} + +//---------------------------------------------------------------------------------- + +void tst_QTcpSocket::setSocketDescriptor() +{ +#ifdef Q_OS_SYMBIAN + QSKIP("adopting open c socket handles is not supported", SkipAll); +#else + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; // this test doesn't make sense with proxies + +#ifdef Q_OS_WIN + // need the dummy to ensure winsock is started + QTcpSocket *dummy = newSocket(); + dummy->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(dummy->waitForConnected()); + + SOCKET sock = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock == INVALID_SOCKET) { + qErrnoWarning(WSAGetLastError(), "INVALID_SOCKET"); + } +#else + SOCKET sock = ::socket(AF_INET, SOCK_STREAM, 0); + + // artificially increase the value of sock + SOCKET sock2 = ::fcntl(sock, F_DUPFD, sock + 50); + ::close(sock); + sock = sock2; +#endif + + QVERIFY(sock != INVALID_SOCKET); + QTcpSocket *socket = newSocket(); + QVERIFY(socket->setSocketDescriptor(sock, QTcpSocket::UnconnectedState)); + QCOMPARE(socket->socketDescriptor(), (int)sock); + + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QCOMPARE(socket->state(), QTcpSocket::HostLookupState); + QCOMPARE(socket->socketDescriptor(), (int)sock); + QVERIFY(socket->waitForConnected(10000)); + // skip this, it has been broken for years, see task 260735 + // if somebody complains, consider fixing it, but it might break existing applications. + QEXPECT_FAIL("", "bug has been around for years, will not fix without need", Continue); + QCOMPARE(socket->socketDescriptor(), (int)sock); + delete socket; +#ifdef Q_OS_WIN + delete dummy; +#endif +#endif +} + +//---------------------------------------------------------------------------------- + +void tst_QTcpSocket::socketDescriptor() +{ + QTcpSocket *socket = newSocket(); + + QCOMPARE(socket->socketDescriptor(), -1); + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY((socket->state() == QAbstractSocket::HostLookupState && socket->socketDescriptor() == -1) || + (socket->state() == QAbstractSocket::ConnectingState && socket->socketDescriptor() != -1)); + QVERIFY(socket->waitForConnected(10000)); + QVERIFY(socket->state() == QAbstractSocket::ConnectedState); + QVERIFY(socket->socketDescriptor() != -1); + + delete socket; +} + +//---------------------------------------------------------------------------------- + +void tst_QTcpSocket::blockingIMAP() +{ + QTcpSocket *socket = newSocket(); + + // Connect + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket->waitForConnected(10000)); + QCOMPARE(socket->state(), QTcpSocket::ConnectedState); + QVERIFY(socket->isValid()); + + // Read greeting + QVERIFY(socket->waitForReadyRead(5000)); + QString s = socket->readLine(); + // only test if an OK was returned, to make the test compatible between different + // IMAP server versions + QCOMPARE(s.left(4).toLatin1().constData(), "* OK"); + + // Write NOOP + QCOMPARE((int) socket->write("1 NOOP\r\n", 8), 8); + QCOMPARE((int) socket->write("2 NOOP\r\n", 8), 8); + + if (!socket->canReadLine()) + QVERIFY(socket->waitForReadyRead(5000)); + + // Read response + s = socket->readLine(); + QCOMPARE(s.toLatin1().constData(), "1 OK Completed\r\n"); + + // Write a third NOOP to verify that write doesn't clear the read buffer + QCOMPARE((int) socket->write("3 NOOP\r\n", 8), 8); + + // Read second response + if (!socket->canReadLine()) + QVERIFY(socket->waitForReadyRead(5000)); + s = socket->readLine(); + QCOMPARE(s.toLatin1().constData(), "2 OK Completed\r\n"); + + // Read third response + if (!socket->canReadLine()) + QVERIFY(socket->waitForReadyRead(5000)); + s = socket->readLine(); + QCOMPARE(s.toLatin1().constData(), "3 OK Completed\r\n"); + + + // Write LOGOUT + QCOMPARE((int) socket->write("4 LOGOUT\r\n", 10), 10); + + if (!socket->canReadLine()) + QVERIFY(socket->waitForReadyRead(5000)); + + // Read two lines of respose + s = socket->readLine(); + QCOMPARE(s.toLatin1().constData(), "* BYE LOGOUT received\r\n"); + + if (!socket->canReadLine()) + QVERIFY(socket->waitForReadyRead(5000)); + + s = socket->readLine(); + QCOMPARE(s.toLatin1().constData(), "4 OK Completed\r\n"); + + // Close the socket + socket->close(); + + // Check that it's closed + QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); + + delete socket; +} + +//---------------------------------------------------------------------------------- + +void tst_QTcpSocket::hostNotFound() +{ + QTcpSocket *socket = newSocket(); + + socket->connectToHost("nosuchserver.troll.no", 80); + QVERIFY(!socket->waitForConnected()); + QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); + QCOMPARE(int(socket->error()), int(QTcpSocket::HostNotFoundError)); + + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::timeoutConnect_data() +{ + QTest::addColumn("address"); + QTest::newRow("host") << QtNetworkSettings::serverName(); + QTest::newRow("ip") << QtNetworkSettings::serverIP().toString(); +} + +void tst_QTcpSocket::timeoutConnect() +{ + QFETCH(QString, address); + QTcpSocket *socket = newSocket(); + + QElapsedTimer timer; + timer.start(); + + // Port 1357 is configured to drop packets on the test server + socket->connectToHost(address, 1357); + QVERIFY(timer.elapsed() < 150); + QVERIFY(!socket->waitForConnected(1000)); //200ms is too short when using SOCKS proxy authentication + QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); + QCOMPARE(int(socket->error()), int(QTcpSocket::SocketTimeoutError)); + + timer.start(); + socket->connectToHost(address, 1357); + QVERIFY(timer.elapsed() < 150); + QTimer::singleShot(50, &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(5); + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(socket->state() == QTcpSocket::ConnectingState + || socket->state() == QTcpSocket::HostLookupState); + socket->abort(); + QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); + QCOMPARE(socket->openMode(), QIODevice::NotOpen); + + delete socket; +} + +//---------------------------------------------------------------------------------- + +void tst_QTcpSocket::nonBlockingIMAP() +{ + QTcpSocket *socket = newSocket(); + connect(socket, SIGNAL(hostFound()), SLOT(nonBlockingIMAP_hostFound())); + connect(socket, SIGNAL(connected()), SLOT(nonBlockingIMAP_connected())); + connect(socket, SIGNAL(disconnected()), SLOT(nonBlockingIMAP_closed())); + connect(socket, SIGNAL(bytesWritten(qint64)), SLOT(nonBlockingIMAP_bytesWritten(qint64))); + connect(socket, SIGNAL(readyRead()), SLOT(nonBlockingIMAP_readyRead())); + nonBlockingIMAP_socket = socket; + + // Connect + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket->state() == QTcpSocket::HostLookupState || + socket->state() == QTcpSocket::ConnectingState); + + enterLoop(30); + if (timeout()) { + QFAIL("Timed out"); + } + + if (socket->state() == QTcpSocket::ConnectingState) { + enterLoop(30); + if (timeout()) { + QFAIL("Timed out"); + } + } + + QCOMPARE(socket->state(), QTcpSocket::ConnectedState); + + enterLoop(30); + if (timeout()) { + QFAIL("Timed out"); + } + + // Read greeting + QVERIFY(!nonBlockingIMAP_data.isEmpty()); + QCOMPARE(nonBlockingIMAP_data.at(0).left(4).toLatin1().constData(), "* OK"); + nonBlockingIMAP_data.clear(); + + nonBlockingIMAP_totalWritten = 0; + + // Write NOOP + QCOMPARE((int) socket->write("1 NOOP\r\n", 8), 8); + + + enterLoop(30); + if (timeout()) { + QFAIL("Timed out"); + } + + QVERIFY(nonBlockingIMAP_totalWritten == 8); + + + enterLoop(30); + if (timeout()) { + QFAIL("Timed out"); + } + + + // Read response + QVERIFY(!nonBlockingIMAP_data.isEmpty()); + QCOMPARE(nonBlockingIMAP_data.at(0).toLatin1().constData(), "1 OK Completed\r\n"); + nonBlockingIMAP_data.clear(); + + + nonBlockingIMAP_totalWritten = 0; + + // Write LOGOUT + QCOMPARE((int) socket->write("2 LOGOUT\r\n", 10), 10); + + enterLoop(30); + if (timeout()) { + QFAIL("Timed out"); + } + + QVERIFY(nonBlockingIMAP_totalWritten == 10); + + // Wait for greeting + enterLoop(30); + if (timeout()) { + QFAIL("Timed out"); + } + + // Read two lines of respose + QCOMPARE(nonBlockingIMAP_data.at(0).toLatin1().constData(), "* BYE LOGOUT received\r\n"); + QCOMPARE(nonBlockingIMAP_data.at(1).toLatin1().constData(), "2 OK Completed\r\n"); + nonBlockingIMAP_data.clear(); + + // Close the socket + socket->close(); + + // Check that it's closed + QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); + + delete socket; +} + +void tst_QTcpSocket::nonBlockingIMAP_hostFound() +{ + exitLoop(); +} + +void tst_QTcpSocket::nonBlockingIMAP_connected() +{ + exitLoop(); +} + +void tst_QTcpSocket::nonBlockingIMAP_readyRead() +{ + while (nonBlockingIMAP_socket->canReadLine()) + nonBlockingIMAP_data.append(nonBlockingIMAP_socket->readLine()); + + exitLoop(); +} + +void tst_QTcpSocket::nonBlockingIMAP_bytesWritten(qint64 written) +{ + nonBlockingIMAP_totalWritten += written; + exitLoop(); +} + +void tst_QTcpSocket::nonBlockingIMAP_closed() +{ +} + +//---------------------------------------------------------------------------------- + +void tst_QTcpSocket::delayedClose() +{ + QTcpSocket *socket = newSocket(); + connect(socket, SIGNAL(connected()), SLOT(nonBlockingIMAP_connected())); + connect(socket, SIGNAL(disconnected()), SLOT(exitLoopSlot())); + + socket->connectToHost(QtNetworkSettings::serverName(), 143); + + enterLoop(30); + if (timeout()) + QFAIL("Timed out"); + + QCOMPARE(socket->state(), QTcpSocket::ConnectedState); + + QCOMPARE((int) socket->write("1 LOGOUT\r\n", 10), 10); + + // Add a huge bulk of data to be written after the logout + // command. The server will shut down after receiving the LOGOUT, + // so this data will not be read. But our close call should + // schedule a delayed close because all the data can not be + // written in one go. + QCOMPARE((int) socket->write(QByteArray(100000, '\n'), 100000), 100000); + + socket->close(); + + QCOMPARE((int) socket->state(), (int) QTcpSocket::ClosingState); + + enterLoop(10); + if (timeout()) + QFAIL("Timed out"); + + QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); + + delete socket; +} + + +//---------------------------------------------------------------------------------- + +QByteArray tst_QTcpSocket::expectedReplyIMAP() +{ + if (expectedReplyIMAP_cached.isEmpty()) { + fetchExpectedReplyIMAP(); + } + + return expectedReplyIMAP_cached; +} + +// Figure out how the current IMAP server responds +void tst_QTcpSocket::fetchExpectedReplyIMAP() +{ + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY2(socket->waitForConnected(10000), qPrintable(socket->errorString())); + QVERIFY2(socket->state() == QTcpSocket::ConnectedState, qPrintable(socket->errorString())); + + QTRY_VERIFY(socket->canReadLine()); + + QByteArray greeting = socket->readLine(); + delete socket; + + QVERIFY2(QtNetworkSettings::compareReplyIMAP(greeting), greeting.constData()); + + expectedReplyIMAP_cached = greeting; +} + +//---------------------------------------------------------------------------------- + +void tst_QTcpSocket::partialRead() +{ + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket->waitForConnected(10000)); + QVERIFY(socket->state() == QTcpSocket::ConnectedState); + char buf[512]; + + QByteArray greeting = expectedReplyIMAP(); + QVERIFY(!greeting.isEmpty()); + + for (int i = 0; i < 10; i += 2) { + while (socket->bytesAvailable() < 2) + QVERIFY(socket->waitForReadyRead(5000)); + QVERIFY(socket->read(buf, 2) == 2); + buf[2] = '\0'; + QCOMPARE((char *)buf, greeting.mid(i, 2).data()); + } + + delete socket; +} + +//---------------------------------------------------------------------------------- + +void tst_QTcpSocket::unget() +{ + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket->waitForConnected(10000)); + QVERIFY(socket->state() == QTcpSocket::ConnectedState); + char buf[512]; + + QByteArray greeting = expectedReplyIMAP(); + QVERIFY(!greeting.isEmpty()); + + for (int i = 0; i < 10; i += 2) { + while (socket->bytesAvailable() < 2) + QVERIFY(socket->waitForReadyRead(10000)); + int bA = socket->bytesAvailable(); + QVERIFY(socket->read(buf, 2) == 2); + buf[2] = '\0'; + QCOMPARE((char *)buf, greeting.mid(i, 2).data()); + QCOMPARE((int)socket->bytesAvailable(), bA - 2); + socket->ungetChar(buf[1]); + socket->ungetChar(buf[0]); + QCOMPARE((int)socket->bytesAvailable(), bA); + QVERIFY(socket->read(buf, 2) == 2); + buf[2] = '\0'; + QCOMPARE((char *)buf, greeting.mid(i, 2).data()); + } + + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::readRegularFile_readyRead() +{ + exitLoop(); +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::readAllAfterClose() +{ + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 143); + connect(socket, SIGNAL(readyRead()), SLOT(readRegularFile_readyRead())); + enterLoop(10); + if (timeout()) + QFAIL("Network operation timed out"); + + socket->close(); + QByteArray array = socket->readAll(); + QCOMPARE(array.size(), 0); + + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::openCloseOpenClose() +{ + QTcpSocket *socket = newSocket(); + + for (int i = 0; i < 3; ++i) { + QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); + QCOMPARE(int(socket->openMode()), int(QIODevice::NotOpen)); + QVERIFY(socket->isSequential()); + QVERIFY(!socket->isOpen()); + QVERIFY(socket->socketType() == QTcpSocket::TcpSocket); + + char c; + QCOMPARE(socket->getChar(&c), false); + QCOMPARE((int) socket->bytesAvailable(), 0); + QCOMPARE(socket->canReadLine(), false); + QCOMPARE(socket->readLine(), QByteArray()); + QCOMPARE(socket->socketDescriptor(), -1); + QCOMPARE((int) socket->localPort(), 0); + QVERIFY(socket->localAddress() == QHostAddress()); + QCOMPARE((int) socket->peerPort(), 0); + QVERIFY(socket->peerAddress() == QHostAddress()); + QCOMPARE(socket->error(), QTcpSocket::UnknownSocketError); + QCOMPARE(socket->errorString(), QString("Unknown error")); + + QVERIFY(socket->state() == QTcpSocket::UnconnectedState); + + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket->waitForConnected(10000)); + socket->close(); + } + + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::connectDisconnectConnectDisconnect() +{ + QTcpSocket *socket = newSocket(); + + for (int i = 0; i < 3; ++i) { + QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); + QVERIFY(socket->socketType() == QTcpSocket::TcpSocket); + + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket->waitForReadyRead(10000)); + QCOMPARE(QString::fromLatin1(socket->read(4)), QString("* OK")); + + socket->disconnectFromHost(); + if (socket->state() != QTcpSocket::UnconnectedState) + QVERIFY(socket->waitForDisconnected(10000)); + QCOMPARE(int(socket->openMode()), int(QIODevice::ReadWrite)); + } + + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::disconnectWhileConnecting_data() +{ + QTest::addColumn("data"); + QTest::addColumn("closeDirectly"); + + QTest::newRow("without-data") << QByteArray() << false; + QTest::newRow("without-data+close") << QByteArray() << true; + QTest::newRow("with-data") << QByteArray("Hello, world!") << false; + QTest::newRow("with-data+close") << QByteArray("Hello, world!") << true; + + QByteArray bigData(1024*1024, '@'); + QTest::newRow("with-big-data") << bigData << false; + QTest::newRow("with-big-data+close") << bigData << true; +} + +void tst_QTcpSocket::disconnectWhileConnecting() +{ + QFETCH(QByteArray, data); + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; //proxy not useful for localhost test case + + QTcpServer server; + QVERIFY(server.listen(QHostAddress::LocalHost)); + + // proceed to the connect-write-disconnect + QTcpSocket *socket = newSocket(); + socket->connectToHost("127.0.0.1", server.serverPort()); + if (!data.isEmpty()) + socket->write(data); + if (socket->state() == QAbstractSocket::ConnectedState) + QSKIP("localhost connections are immediate, test case is invalid", SkipSingle); + + QFETCH(bool, closeDirectly); + if (closeDirectly) { + socket->close(); + QCOMPARE(int(socket->openMode()), int(QIODevice::NotOpen)); + } else { + socket->disconnectFromHost(); + } + + connect(socket, SIGNAL(disconnected()), SLOT(exitLoopSlot())); +#ifndef Q_OS_SYMBIAN + enterLoop(10); +#else + enterLoop(30); +#endif + QVERIFY2(!timeout(), "Network timeout"); + QVERIFY(socket->state() == QAbstractSocket::UnconnectedState); + if (!closeDirectly) { + QCOMPARE(int(socket->openMode()), int(QIODevice::ReadWrite)); + socket->close(); + } + QCOMPARE(int(socket->openMode()), int(QIODevice::NotOpen)); + + // accept the other side and verify that it was sent properly: + QVERIFY(server.hasPendingConnections() || server.waitForNewConnection(0)); + QTcpSocket *othersocket = server.nextPendingConnection(); + if (othersocket->state() != QAbstractSocket::UnconnectedState) + QVERIFY2(othersocket->waitForDisconnected(10000), "Network timeout"); + QVERIFY(othersocket->state() == QAbstractSocket::UnconnectedState); + QCOMPARE(othersocket->readAll(), data); + + delete socket; + delete othersocket; +} + +//---------------------------------------------------------------------------------- +class ReceiverThread: public QThread +{ + QTcpServer *server; +public: + int serverPort; + bool ok; + QByteArray receivedData; + volatile bool quit; + + ReceiverThread() + : server(0), ok(false), quit(false) + { } + + ~ReceiverThread() { } + + bool listen() + { + server = new QTcpServer; + if (!server->listen(QHostAddress::LocalHost)) + return false; + serverPort = server->serverPort(); + server->moveToThread(this); + return true; + } + + static void cleanup(void *ptr) + { + ReceiverThread* self = reinterpret_cast(ptr); + self->quit = true; + self->wait(30000); + delete self; + } + +protected: + void run() + { + bool timedOut = false; + while (!quit) { +#ifndef Q_OS_SYMBIAN + if (server->waitForNewConnection(500, &timedOut)) +#else + if (server->waitForNewConnection(5000, &timedOut)) +#endif + break; + if (!timedOut) + return; + } + + QTcpSocket *socket = server->nextPendingConnection(); + while (!quit) { +#ifndef Q_OS_SYMBIAN + if (socket->waitForDisconnected(500)) +#else + if (socket->waitForDisconnected(5000)) +#endif + break; + if (socket->error() != QAbstractSocket::SocketTimeoutError) + return; + } + + if (!quit) { + receivedData = socket->readAll(); + ok = true; + } + delete socket; + delete server; + server = 0; + } +}; + +void tst_QTcpSocket::disconnectWhileConnectingNoEventLoop_data() +{ + disconnectWhileConnecting_data(); +} + +void tst_QTcpSocket::disconnectWhileConnectingNoEventLoop() +{ + QFETCH(QByteArray, data); + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; //proxy not useful for localhost test case + + QScopedPointer thread (new ReceiverThread); + QVERIFY(thread->listen()); + thread->start(); + + // proceed to the connect-write-disconnect + QTcpSocket *socket = newSocket(); + socket->connectToHost("127.0.0.1", thread->serverPort); + if (!data.isEmpty()) + socket->write(data); + if (socket->state() == QAbstractSocket::ConnectedState) { + QSKIP("localhost connections are immediate, test case is invalid", SkipSingle); + } + + QFETCH(bool, closeDirectly); + if (closeDirectly) { + socket->close(); + QCOMPARE(int(socket->openMode()), int(QIODevice::NotOpen)); + } else { + socket->disconnectFromHost(); + } + +#ifndef Q_OS_SYMBIAN + QVERIFY2(socket->waitForDisconnected(10000), "Network timeout"); +#else + QVERIFY2(socket->waitForDisconnected(30000), "Network timeout"); +#endif + QVERIFY(socket->state() == QAbstractSocket::UnconnectedState); + if (!closeDirectly) { + QCOMPARE(int(socket->openMode()), int(QIODevice::ReadWrite)); + socket->close(); + } + QCOMPARE(int(socket->openMode()), int(QIODevice::NotOpen)); + delete socket; + + // check if the other side received everything ok + QVERIFY(thread->wait(30000)); + QVERIFY(thread->ok); + QCOMPARE(thread->receivedData, data); +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::disconnectWhileLookingUp_data() +{ + QTest::addColumn("doClose"); + + QTest::newRow("disconnect") << false; + QTest::newRow("close") << true; +} + +void tst_QTcpSocket::disconnectWhileLookingUp() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; // we let the proxies do the lookup now + + // just connect and disconnect, then make sure nothing weird happened + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 21); + + // check that connect is in progress + QVERIFY(socket->state() != QAbstractSocket::UnconnectedState); + + QFETCH(bool, doClose); + if (doClose) { + socket->close(); + QVERIFY(socket->openMode() == QIODevice::NotOpen); + } else { + socket->disconnectFromHost(); + QVERIFY(socket->openMode() == QIODevice::ReadWrite); + } + + // let anything queued happen + QEventLoop loop; +#ifndef Q_OS_SYMBIAN + QTimer::singleShot(50, &loop, SLOT(quit())); +#else + QTimer::singleShot(5000, &loop, SLOT(quit())); +#endif + loop.exec(); + + // recheck + if (doClose) { + QVERIFY(socket->openMode() == QIODevice::NotOpen); + } else { + QVERIFY(socket->openMode() == QIODevice::ReadWrite); + } + + QVERIFY(socket->state() == QAbstractSocket::UnconnectedState); +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::downloadBigFile() +{ + if (tmpSocket) + delete tmpSocket; + tmpSocket = newSocket(); + + connect(tmpSocket, SIGNAL(connected()), SLOT(exitLoopSlot())); + connect(tmpSocket, SIGNAL(readyRead()), SLOT(downloadBigFileSlot())); + connect(tmpSocket, SIGNAL(disconnected()), SLOT(exitLoopSlot())); + + tmpSocket->connectToHost(QtNetworkSettings::serverName(), 80); + + enterLoop(30); + if (timeout()) { + delete tmpSocket; + tmpSocket = 0; + QFAIL("Network operation timed out"); + } + + QByteArray hostName = QtNetworkSettings::serverName().toLatin1(); + QVERIFY(tmpSocket->state() == QAbstractSocket::ConnectedState); + QVERIFY(tmpSocket->write("GET /qtest/mediumfile HTTP/1.0\r\n") > 0); + QVERIFY(tmpSocket->write("HOST: ") > 0); + QVERIFY(tmpSocket->write(hostName.data()) > 0); + QVERIFY(tmpSocket->write("\r\n") > 0); + QVERIFY(tmpSocket->write("\r\n") > 0); + + bytesAvailable = 0; + expectedLength = 0; + readingBody = false; + + QTime stopWatch; + stopWatch.start(); + + enterLoop(600); + if (timeout()) { + delete tmpSocket; + tmpSocket = 0; + if (bytesAvailable > 0) + qDebug("Slow Connection, only downloaded %ld of %d", long(bytesAvailable), 10000281); + QFAIL("Network operation timed out"); + } + + QCOMPARE(bytesAvailable, expectedLength); + + qDebug("\t\t%.1fMB/%.1fs: %.1fMB/s", + bytesAvailable / (1024.0 * 1024.0), + stopWatch.elapsed() / 1024.0, + (bytesAvailable / (stopWatch.elapsed() / 1000.0)) / (1024 * 1024)); + + delete tmpSocket; + tmpSocket = 0; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::exitLoopSlot() +{ + exitLoop(); +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::downloadBigFileSlot() +{ + if (!readingBody) { + while (tmpSocket->canReadLine()) { + QByteArray array = tmpSocket->readLine(); + if (array.startsWith("Content-Length")) + expectedLength = array.simplified().split(' ').at(1).toInt(); + if (array == "\r\n") { + readingBody = true; + break; + } + } + } + if (readingBody) { + bytesAvailable += tmpSocket->readAll().size(); + if (bytesAvailable == expectedLength) + exitLoop(); + } +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::readLine() +{ + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket->waitForConnected(5000)); + + while (!socket->canReadLine()) + QVERIFY(socket->waitForReadyRead(10000)); + + char buffer[1024]; + + qint64 linelen = socket->readLine(buffer, sizeof(buffer)); + QVERIFY(linelen >= 3); + QVERIFY(linelen < 1024); + + QByteArray reply = QByteArray::fromRawData(buffer, linelen); + QCOMPARE((int) buffer[linelen-2], (int) '\r'); + QCOMPARE((int) buffer[linelen-1], (int) '\n'); + QCOMPARE((int) buffer[linelen], (int) '\0'); + + QVERIFY2(QtNetworkSettings::compareReplyIMAP(reply), reply.constData()); + + QCOMPARE(socket->write("1 NOOP\r\n"), qint64(8)); + + while (socket->bytesAvailable() < 10) + QVERIFY(socket->waitForReadyRead(10000)); + + QCOMPARE(socket->readLine(buffer, 11), qint64(10)); + QCOMPARE((const char *)buffer, "1 OK Compl"); + + while (socket->bytesAvailable() < 6) + QVERIFY(socket->waitForReadyRead(10000)); + + QCOMPARE(socket->readLine(buffer, 11), qint64(6)); + QCOMPARE((const char *)buffer, "eted\r\n"); + + QVERIFY(!socket->waitForReadyRead(100)); + QCOMPARE(socket->readLine(buffer, sizeof(buffer)), qint64(0)); + QVERIFY(socket->error() == QAbstractSocket::SocketTimeoutError + || socket->error() == QAbstractSocket::RemoteHostClosedError); + QCOMPARE(socket->bytesAvailable(), qint64(0)); + + socket->close(); + QCOMPARE(socket->readLine(buffer, sizeof(buffer)), qint64(-1)); + + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::readLineString() +{ + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket->waitForReadyRead(10000)); + + QByteArray arr = socket->readLine(); + QVERIFY2(QtNetworkSettings::compareReplyIMAP(arr), arr.constData()); + + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::readChunks() +{ + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket->waitForConnected(10000)); + QVERIFY(socket->waitForReadyRead(5000)); + + char buf[4096]; + memset(buf, '@', sizeof(buf)); + qint64 dataLength = socket->read(buf, sizeof(buf)); + QVERIFY(dataLength > 0); + + QCOMPARE(buf[dataLength - 2], '\r'); + QCOMPARE(buf[dataLength - 1], '\n'); + QCOMPARE(buf[dataLength], '@'); + + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::waitForBytesWritten() +{ + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 80); + QVERIFY(socket->waitForConnected(10000)); + + socket->write("GET / HTTP/1.0\r\n\r\n"); + qint64 toWrite = socket->bytesToWrite(); + QVERIFY(socket->waitForBytesWritten(5000)); + QVERIFY(toWrite > socket->bytesToWrite()); + + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::waitForBytesWrittenMinusOne() +{ + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 80); + QVERIFY(socket->waitForConnected(10000)); + + socket->write("GET / HTTP/1.0\r\n\r\n"); + qint64 toWrite = socket->bytesToWrite(); + QVERIFY(socket->waitForBytesWritten(-1)); + QVERIFY(toWrite > socket->bytesToWrite()); + + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::waitForReadyRead() +{ + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 80); + socket->write("GET / HTTP/1.0\r\n\r\n"); + QVERIFY(socket->waitForReadyRead(5000)); + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::waitForReadyReadMinusOne() +{ + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 80); + socket->write("GET / HTTP/1.0\r\n\r\n"); + QVERIFY(socket->waitForReadyRead(-1)); + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::flush() +{ + QTcpSocket *socket = newSocket(); + socket->flush(); + + connect(socket, SIGNAL(connected()), SLOT(exitLoopSlot())); + socket->connectToHost(QtNetworkSettings::serverName(), 143); + enterLoop(60); + QVERIFY(socket->isOpen()); + + socket->write("1 LOGOUT\r\n"); + QCOMPARE(socket->bytesToWrite(), qint64(10)); + socket->flush(); + QCOMPARE(socket->bytesToWrite(), qint64(0)); + socket->close(); + + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::synchronousApi() +{ + QTcpSocket *ftpSocket = newSocket(); + ftpSocket->connectToHost(QtNetworkSettings::serverName(), 21); + ftpSocket->write("QUIT\r\n"); + QVERIFY(ftpSocket->waitForDisconnected(10000)); + QVERIFY(ftpSocket->bytesAvailable() > 0); + QByteArray arr = ftpSocket->readAll(); + QVERIFY(arr.size() > 0); + delete ftpSocket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::dontCloseOnTimeout() +{ + QTcpServer server; + server.setProxy(QNetworkProxy(QNetworkProxy::NoProxy)); + QVERIFY(server.listen()); + + QHostAddress serverAddress = QHostAddress::LocalHost; + if (!(server.serverAddress() == QHostAddress::AnyIPv4) && !(server.serverAddress() == QHostAddress::AnyIPv6)) + serverAddress = server.serverAddress(); + + QTcpSocket *socket = newSocket(); + socket->connectToHost(serverAddress, server.serverPort()); +#ifndef Q_OS_SYMBIAN + QVERIFY(!socket->waitForReadyRead(100)); +#else + QVERIFY(!socket->waitForReadyRead(5000)); +#endif + QCOMPARE(socket->error(), QTcpSocket::SocketTimeoutError); + QVERIFY(socket->isOpen()); + +#ifndef Q_OS_SYMBIAN + QVERIFY(!socket->waitForDisconnected(100)); +#else + QVERIFY(!socket->waitForDisconnected(5000)); +#endif + QCOMPARE(socket->error(), QTcpSocket::SocketTimeoutError); + QVERIFY(socket->isOpen()); + + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::recursiveReadyRead() +{ + QTcpSocket *smtp = newSocket(); + connect(smtp, SIGNAL(connected()), SLOT(exitLoopSlot())); + connect(smtp, SIGNAL(readyRead()), SLOT(recursiveReadyReadSlot())); + tmpSocket = smtp; + + QSignalSpy spy(smtp, SIGNAL(readyRead())); + + smtp->connectToHost("smtp.trolltech.com", 25); + enterLoop(30); + QVERIFY2(!timeout(), + "Timed out when connecting to smtp.trolltech.com:25"); + + enterLoop(30); + QVERIFY2(!timeout(), + "Timed out when waiting for the readyRead() signal"); + + QCOMPARE(spy.count(), 1); + + delete smtp; +} + +void tst_QTcpSocket::recursiveReadyReadSlot() +{ + // make sure the server spits out more data + tmpSocket->write("NOOP\r\n"); + tmpSocket->flush(); + + // indiscriminately enter the event loop and start processing + // events again. but oops! future socket notifications will cause + // undesired recursive behavior. Unless QTcpSocket is smart, which + // it of course is. :-) + QEventLoop loop; + for (int i = 0; i < 100; ++i) + loop.processEvents(); + + // all we really wanted to do was process some events, then exit + // the loop + exitLoop(); +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::atEnd() +{ + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 21); + + QVERIFY(socket->waitForReadyRead(15000)); + QTextStream stream(socket); + QVERIFY(!stream.atEnd()); + QString greeting = stream.readLine(); + QVERIFY(stream.atEnd()); + + // Test server must use some vsFTPd 2.x.x version + QVERIFY2(greeting.length() == sizeof("220 (vsFTPd 2.x.x)")-1, qPrintable(greeting)); + QVERIFY2(greeting.startsWith("220 (vsFTPd 2."), qPrintable(greeting)); + QVERIFY2(greeting.endsWith(")"), qPrintable(greeting)); + + delete socket; +} + +class TestThread : public QThread +{ + Q_OBJECT + +public: + inline QByteArray data() const + { + return socketData; + } + +protected: + inline void run() + { +#ifndef QT_NO_OPENSSL + QFETCH_GLOBAL(bool, ssl); + if (ssl) + socket = new QSslSocket; + else +#endif + socket = new QTcpSocket; + connect(socket, SIGNAL(readyRead()), this, SLOT(getData()), Qt::DirectConnection); + connect(socket, SIGNAL(disconnected()), this, SLOT(closed()), Qt::DirectConnection); + connect(socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), Qt::DirectConnection); + + socket->connectToHost(QtNetworkSettings::serverName(), 21); + socket->write("QUIT\r\n"); + exec(); + + delete socket; + } + +private slots: + inline void getData() + { + socketData += socket->readAll(); + } + + inline void closed() + { + quit(); + } + inline void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth) + { + auth->setUser("qsockstest"); + auth->setPassword("password"); + } +private: + int exitCode; + QTcpSocket *socket; + QByteArray socketData; +}; + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::socketInAThread() +{ + for (int i = 0; i < 3; ++i) { + TestThread thread; + thread.start(); + QVERIFY(thread.wait(15000)); + QByteArray data = thread.data(); + QVERIFY2(QtNetworkSettings::compareReplyFtp(data), data.constData()); + } +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::socketsInThreads() +{ + for (int i = 0; i < 3; ++i) { + TestThread thread1; + TestThread thread2; + TestThread thread3; + + thread1.start(); + thread2.start(); + thread3.start(); + + QVERIFY(thread2.wait(15000)); + QVERIFY(thread3.wait(15000)); + QVERIFY(thread1.wait(15000)); + + QByteArray data1 = thread1.data(); + QByteArray data2 = thread2.data(); + QByteArray data3 = thread3.data(); + + QVERIFY2(QtNetworkSettings::compareReplyFtp(data1), data1.constData()); + QVERIFY2(QtNetworkSettings::compareReplyFtp(data2), data2.constData()); + QVERIFY2(QtNetworkSettings::compareReplyFtp(data3), data3.constData()); + } +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::waitForReadyReadInASlot() +{ + QTcpSocket *socket = newSocket(); + tmpSocket = socket; + connect(socket, SIGNAL(connected()), this, SLOT(waitForReadyReadInASlotSlot())); + + socket->connectToHost(QtNetworkSettings::serverName(), 80); + socket->write("GET / HTTP/1.0\r\n\r\n"); + + enterLoop(30); + QVERIFY(!timeout()); + + delete socket; +} + +void tst_QTcpSocket::waitForReadyReadInASlotSlot() +{ + QVERIFY(tmpSocket->waitForReadyRead(10000)); + exitLoop(); +} + +class RemoteCloseErrorServer : public QTcpServer +{ + Q_OBJECT +public: + RemoteCloseErrorServer() + { + connect(this, SIGNAL(newConnection()), + this, SLOT(getConnection())); + } + +private slots: + void getConnection() + { + tst_QTcpSocket::exitLoop(); + } +}; + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::remoteCloseError() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; //proxy not useful for localhost test case + RemoteCloseErrorServer server; + QVERIFY(server.listen(QHostAddress::LocalHost)); + + QCoreApplication::instance()->processEvents(); + + QTcpSocket *clientSocket = newSocket(); + connect(clientSocket, SIGNAL(readyRead()), this, SLOT(exitLoopSlot())); + + clientSocket->connectToHost(server.serverAddress(), server.serverPort()); + + enterLoop(30); + QVERIFY(!timeout()); + + QVERIFY(server.hasPendingConnections()); + QTcpSocket *serverSocket = server.nextPendingConnection(); + connect(clientSocket, SIGNAL(disconnected()), this, SLOT(exitLoopSlot())); + + serverSocket->write("Hello"); + + enterLoop(30); + QVERIFY(!timeout()); + + QCOMPARE(clientSocket->bytesAvailable(), qint64(5)); + + qRegisterMetaType("QAbstractSocket::SocketError"); + QSignalSpy errorSpy(clientSocket, SIGNAL(error(QAbstractSocket::SocketError))); + QSignalSpy disconnectedSpy(clientSocket, SIGNAL(disconnected())); + + clientSocket->write("World"); + serverSocket->disconnectFromHost(); + + tmpSocket = clientSocket; + connect(clientSocket, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(remoteCloseErrorSlot())); + + enterLoop(30); + QVERIFY(!timeout()); + + QCOMPARE(disconnectedSpy.count(), 1); + QCOMPARE(errorSpy.count(), 1); + QCOMPARE(clientSocket->error(), QAbstractSocket::RemoteHostClosedError); + + delete serverSocket; + + clientSocket->connectToHost(server.serverAddress(), server.serverPort()); + + enterLoop(30); + QVERIFY(!timeout()); + + QVERIFY(server.hasPendingConnections()); + serverSocket = server.nextPendingConnection(); + serverSocket->disconnectFromHost(); + + enterLoop(30); + QVERIFY(!timeout()); + + QCOMPARE(clientSocket->state(), QAbstractSocket::UnconnectedState); + + delete clientSocket; +} + +void tst_QTcpSocket::remoteCloseErrorSlot() +{ + QCOMPARE(tmpSocket->state(), QAbstractSocket::ConnectedState); + static_cast(sender())->close(); +} + +void tst_QTcpSocket::messageBoxSlot() +{ +#if !defined(Q_OS_VXWORKS) // no gui + QTcpSocket *socket = qobject_cast(sender()); + socket->deleteLater(); + QMessageBox box; + QTimer::singleShot(100, &box, SLOT(close())); + + // This should not delete the socket + box.exec(); + + // Fire a non-0 singleshot to leave time for the delete + QTimer::singleShot(250, this, SLOT(exitLoopSlot())); +#endif +} +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::openMessageBoxInErrorSlot() +{ +#if defined(Q_OS_VXWORKS) // no gui + QSKIP("no default gui available on VxWorks", SkipAll); +#else + QTcpSocket *socket = newSocket(); + QPointer p(socket); + connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(messageBoxSlot())); + + socket->connectToHost("hostnotfoundhostnotfound.troll.no", 9999); // Host not found, fyi + enterLoop(30); + QVERIFY(!p); +#endif +} + +//---------------------------------------------------------------------------------- +#ifndef Q_OS_WIN +void tst_QTcpSocket::connectToLocalHostNoService() +{ + // This test was created after we received a report that claimed + // QTcpSocket would crash if trying to connect to "localhost" on a random + // port with no service listening. + QTcpSocket *socket = newSocket(); + socket->connectToHost("localhost", 31415); // no service running here, one suspects + + while(socket->state() == QTcpSocket::HostLookupState || socket->state() == QTcpSocket::ConnectingState) { + QTest::qWait(100); + } + QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); + delete socket; +} +#endif + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::waitForConnectedInHostLookupSlot() +{ + // This test tries to reproduce the problem where waitForConnected() is + // called at a point where the host lookup is already done. QTcpSocket + // will try to abort the "pending lookup", but since it's already done and + // the queued signal is already underway, we will receive the signal after + // waitForConnected() has returned, and control goes back to the event + // loop. When the signal has been received, the connection is torn down, + // then reopened. Yikes. If we reproduce this by calling + // waitForConnected() inside hostLookupSlot(), it will even crash. + tmpSocket = newSocket(); + QEventLoop loop; + connect(tmpSocket, SIGNAL(connected()), &loop, SLOT(quit())); + QTimer timer; + connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); + QSignalSpy timerSpy(&timer, SIGNAL(timeout())); + timer.start(15000); + + connect(tmpSocket, SIGNAL(hostFound()), this, SLOT(hostLookupSlot())); + tmpSocket->connectToHost(QtNetworkSettings::serverName(), 143); + + // only execute the loop if not already connected + if (tmpSocket->state() != QAbstractSocket::ConnectedState) + loop.exec(); + + QCOMPARE(timerSpy.count(), 0); + + delete tmpSocket; +} + +void tst_QTcpSocket::hostLookupSlot() +{ + // This will fail to cancel the pending signal + QVERIFY(tmpSocket->waitForConnected(10000)); +} + +class Foo : public QObject +{ + Q_OBJECT + QTcpSocket *sock; +public: + bool attemptedToConnect; + bool networkTimeout; + int count; + + inline Foo(QObject *parent = 0) : QObject(parent) + { + attemptedToConnect = false; + networkTimeout = false; + count = 0; +#ifndef QT_NO_OPENSSL + QFETCH_GLOBAL(bool, ssl); + if (ssl) + sock = new QSslSocket; + else +#endif + sock = new QTcpSocket; + connect(sock, SIGNAL(connected()), this, SLOT(connectedToIt())); + connect(sock, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + } + + inline ~Foo() + { + delete sock; + } + +public slots: + inline void connectedToIt() + { count++; } + + inline void doIt() + { + attemptedToConnect = true; + sock->connectToHost(QtNetworkSettings::serverName(), 80); + +#ifdef Q_OS_MAC + pthread_yield_np(); +#elif defined Q_OS_LINUX + pthread_yield(); +#endif + if (!sock->waitForConnected()) { + networkTimeout = true; + } + tst_QTcpSocket::exitLoop(); + } + + inline void exitLoop() + { + tst_QTcpSocket::exitLoop(); + } + + inline void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth) + { + auth->setUser("qsockstest"); + auth->setPassword("password"); + } +}; + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::waitForConnectedInHostLookupSlot2() +{ +#if defined(Q_OS_WIN) || defined(Q_OS_VXWORKS) + QSKIP("waitForConnectedInHostLookupSlot2 is not run on Windows and VxWorks", SkipAll); +#else + + Foo foo; + QPushButton top("Go", 0); + top.show(); + connect(&top, SIGNAL(clicked()), &foo, SLOT(doIt())); + + QTimer::singleShot(100, &top, SLOT(animateClick())); + QTimer::singleShot(5000, &foo, SLOT(exitLoop())); + + enterLoop(30); + if (timeout() || foo.networkTimeout) + QFAIL("Network timeout"); + + QVERIFY(foo.attemptedToConnect); + QCOMPARE(foo.count, 1); +#endif +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::readyReadSignalsAfterWaitForReadyRead() +{ + QTcpSocket *socket = newSocket(); + + QSignalSpy readyReadSpy(socket, SIGNAL(readyRead())); + + // Connect + socket->connectToHost(QtNetworkSettings::serverName(), 143); + + // Wait for the read + QVERIFY(socket->waitForReadyRead(10000)); + + QCOMPARE(readyReadSpy.count(), 1); + + QString s = socket->readLine(); + QVERIFY2(QtNetworkSettings::compareReplyIMAP(s.toLatin1()), s.toLatin1().constData()); + QCOMPARE(socket->bytesAvailable(), qint64(0)); + + QCoreApplication::instance()->processEvents(); + QCOMPARE(socket->bytesAvailable(), qint64(0)); + QCOMPARE(readyReadSpy.count(), 1); + + delete socket; +} + +class TestThread2 : public QThread +{ + Q_OBJECT +public: + void run() + { + QFile fileWriter("fifo"); + QVERIFY(fileWriter.open(QFile::WriteOnly)); + QCOMPARE(fileWriter.write(QByteArray(32, '@')), qint64(32)); + QCOMPARE(fileWriter.write(QByteArray(32, '@')), qint64(32)); + QCOMPARE(fileWriter.write(QByteArray(32, '@')), qint64(32)); + QCOMPARE(fileWriter.write(QByteArray(32, '@')), qint64(32)); + } +}; + +//---------------------------------------------------------------------------------- +#ifdef Q_OS_LINUX +void tst_QTcpSocket::linuxKernelBugLocalSocket() +{ + QFile::remove("fifo"); + mkfifo("fifo", 0666); + + TestThread2 test; + test.start(); + + QFile fileReader("fifo"); + QVERIFY(fileReader.open(QFile::ReadOnly)); + + test.wait(); + + QTcpSocket *socket = newSocket(); + socket->setSocketDescriptor(fileReader.handle()); + QVERIFY(socket->waitForReadyRead(5000)); + QCOMPARE(socket->bytesAvailable(), qint64(128)); + + QFile::remove("fifo"); + + delete socket; +} +#endif + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::abortiveClose() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; //proxy not useful for localhost test case + QTcpServer server; + QVERIFY(server.listen(QHostAddress::LocalHost)); + connect(&server, SIGNAL(newConnection()), this, SLOT(exitLoopSlot())); + + QTcpSocket *clientSocket = newSocket(); + clientSocket->connectToHost(server.serverAddress(), server.serverPort()); + + enterLoop(10); + QVERIFY(server.hasPendingConnections()); + + QVERIFY(tmpSocket = server.nextPendingConnection()); + + qRegisterMetaType("QAbstractSocket::SocketError"); + QSignalSpy readyReadSpy(clientSocket, SIGNAL(readyRead())); + QSignalSpy errorSpy(clientSocket, SIGNAL(error(QAbstractSocket::SocketError))); + + connect(clientSocket, SIGNAL(disconnected()), this, SLOT(exitLoopSlot())); + QTimer::singleShot(0, this, SLOT(abortiveClose_abortSlot())); + + enterLoop(5); + + QCOMPARE(readyReadSpy.count(), 0); + QCOMPARE(errorSpy.count(), 1); + + QCOMPARE(*static_cast(errorSpy.at(0).at(0).constData()), + int(QAbstractSocket::RemoteHostClosedError)); + + delete clientSocket; +} + +void tst_QTcpSocket::abortiveClose_abortSlot() +{ + tmpSocket->abort(); +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::localAddressEmptyOnBSD() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; //proxy not useful for localhost test case + QTcpServer server; + QVERIFY(server.listen(QHostAddress::LocalHost)); + + QTcpSocket *tcpSocket = 0; + // we try 10 times, but note that this doesn't always provoke the bug + for (int i = 0; i < 10; ++i) { + delete tcpSocket; + tcpSocket = newSocket(); + tcpSocket->connectToHost(QHostAddress::LocalHost, server.serverPort()); + if (!tcpSocket->waitForConnected(0)) { + // to provoke the bug, we need a local socket that connects immediately + // --i; + tcpSocket->abort(); + if (tcpSocket->state() != QTcpSocket::UnconnectedState) + QVERIFY(tcpSocket->waitForDisconnected(-1)); + continue; + } + QCOMPARE(tcpSocket->localAddress(), QHostAddress(QHostAddress::LocalHost)); + } + delete tcpSocket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::zeroAndMinusOneReturns() +{ + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 80); + socket->write("GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n"); + QVERIFY(socket->waitForReadyRead(15000)); + + char c[16]; + QVERIFY(socket->getChar(c)); + QCOMPARE(socket->read(c, 16), qint64(16)); + QVERIFY(socket->readLine(c, 16) > 0); + QVERIFY(!socket->readAll().isEmpty()); + + // the last operation emptied the read buffer + // all read operations from this point on should fail + // with return 0 because the socket is still open + QVERIFY(socket->readAll().isEmpty()); + QCOMPARE(socket->read(c, 16), qint64(0)); + QCOMPARE(socket->readLine(c, 16), qint64(0)); + QVERIFY(!socket->getChar(c)); + + socket->write("GET / HTTP/1.0\r\n\r\n"); + QVERIFY(socket->waitForDisconnected(15000)); + QCOMPARE(socket->error(), QAbstractSocket::RemoteHostClosedError); + + QCOMPARE(socket->write("BLUBBER"), qint64(-1)); + QVERIFY(socket->getChar(c)); + QCOMPARE(socket->read(c, 16), qint64(16)); + QVERIFY(socket->readLine(c, 16) > 0); + QVERIFY(!socket->readAll().isEmpty()); + + // the last operation emptied the read buffer + // all read operations from this point on should fail + // with return -1 because the socket is not connected + QVERIFY(socket->readAll().isEmpty()); + QCOMPARE(socket->read(c, 16), qint64(-1)); + QCOMPARE(socket->readLine(c, 16), qint64(-1)); + QVERIFY(!socket->getChar(c)); + QVERIFY(!socket->putChar('a')); + + socket->close(); + + // now the QIODevice is closed, which means getChar complains + QCOMPARE(socket->write("BLUBBER"), qint64(-1)); + QCOMPARE(socket->read(c, 16), qint64(-1)); + QCOMPARE(socket->readLine(c, 16), qint64(-1)); + QVERIFY(!socket->getChar(c)); + QVERIFY(!socket->putChar('a')); + + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::connectionRefused() +{ + qRegisterMetaType("QAbstractSocket::SocketError"); + qRegisterMetaType("QAbstractSocket::SocketState"); + + QTcpSocket *socket = newSocket(); + QSignalSpy stateSpy(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState))); + QSignalSpy errorSpy(socket, SIGNAL(error(QAbstractSocket::SocketError))); + connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), + &QTestEventLoop::instance(), SLOT(exitLoop())); + + socket->connectToHost(QtNetworkSettings::serverName(), 144); + + enterLoop(10); + disconnect(socket, SIGNAL(error(QAbstractSocket::SocketError)), + &QTestEventLoop::instance(), SLOT(exitLoop())); + QVERIFY2(!timeout(), "Network timeout"); + + QCOMPARE(socket->state(), QAbstractSocket::UnconnectedState); + QCOMPARE(socket->error(), QAbstractSocket::ConnectionRefusedError); + + QCOMPARE(stateSpy.count(), 3); + QCOMPARE(qVariantValue(stateSpy.at(0).at(0)), QAbstractSocket::HostLookupState); + QCOMPARE(qVariantValue(stateSpy.at(1).at(0)), QAbstractSocket::ConnectingState); + QCOMPARE(qVariantValue(stateSpy.at(2).at(0)), QAbstractSocket::UnconnectedState); + QCOMPARE(errorSpy.count(), 1); + + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::suddenRemoteDisconnect_data() +{ + QTest::addColumn("client"); + QTest::addColumn("server"); + + QTest::newRow("Qt4 Client <-> Qt4 Server") << QString::fromLatin1("qt4client") << QString::fromLatin1("qt4server"); +} + +void tst_QTcpSocket::suddenRemoteDisconnect() +{ +#if defined( Q_OS_SYMBIAN ) + QSKIP("Symbian: QProcess IO is not yet supported, fix when supported", SkipAll); +#else + QFETCH(QString, client); + QFETCH(QString, server); + + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + QFETCH_GLOBAL(bool, ssl); + if (ssl) + return; + + // Start server + QProcess serverProcess; + serverProcess.setReadChannel(QProcess::StandardError); + serverProcess.start(QString::fromLatin1("stressTest/stressTest %1").arg(server), + QIODevice::ReadWrite | QIODevice::Text); + while (!serverProcess.canReadLine()) + QVERIFY(serverProcess.waitForReadyRead(10000)); + QCOMPARE(serverProcess.readLine().data(), (server.toLatin1() + "\n").data()); + + // Start client + QProcess clientProcess; + clientProcess.setReadChannel(QProcess::StandardError); + clientProcess.start(QString::fromLatin1("stressTest/stressTest %1").arg(client), + QIODevice::ReadWrite | QIODevice::Text); + while (!clientProcess.canReadLine()) + QVERIFY(clientProcess.waitForReadyRead(10000)); + QCOMPARE(clientProcess.readLine().data(), (client.toLatin1() + "\n").data()); + + // Let them play for a while + qDebug("Running stress test for 5 seconds"); + QEventLoop loop; + connect(&serverProcess, SIGNAL(finished(int)), &loop, SLOT(quit())); + connect(&clientProcess, SIGNAL(finished(int)), &loop, SLOT(quit())); + QTime stopWatch; + stopWatch.start(); + QTimer::singleShot(20000, &loop, SLOT(quit())); + + while ((serverProcess.state() == QProcess::Running + || clientProcess.state() == QProcess::Running) && stopWatch.elapsed() < 20000) + loop.exec(); + + QVERIFY(stopWatch.elapsed() < 20000); + + // Check that both exited normally. + QCOMPARE(clientProcess.readAll().constData(), "SUCCESS\n"); + QCOMPARE(serverProcess.readAll().constData(), "SUCCESS\n"); +#endif +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::connectToMultiIP() +{ + QSKIP("TODO: setup DNS in the new network", SkipAll); + +#if defined(Q_OS_VXWORKS) + QSKIP("VxSim in standard config doesn't even run a DNS resolver", SkipAll); +#else + QFETCH_GLOBAL(bool, ssl); + if (ssl) + return; + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + QSKIP("This test takes too long if we also add the proxies.", SkipSingle); + + qDebug("Please wait, this test can take a while..."); + + QTcpSocket *socket = newSocket(); + // rationale: this domain resolves to 3 A-records, 2 of them are + // invalid. QTcpSocket should never spend more than 30 seconds per IP, and + // 30s*2 = 60s. + QTime stopWatch; + stopWatch.start(); + socket->connectToHost("multi.dev.troll.no", 80); + QVERIFY(socket->waitForConnected(60500)); + QVERIFY(stopWatch.elapsed() < 70000); + socket->abort(); + + stopWatch.restart(); + socket->connectToHost("multi.dev.troll.no", 81); + QVERIFY(!socket->waitForConnected(2000)); + QVERIFY(stopWatch.elapsed() < 2000); + QCOMPARE(socket->error(), QAbstractSocket::SocketTimeoutError); + + delete socket; +#endif +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::moveToThread0() +{ + QFETCH_GLOBAL(int, proxyType); + if (proxyType & AuthMask) + return; + + { + // Case 1: Moved after connecting, before waiting for connection. + QTcpSocket *socket = newSocket();; + socket->connectToHost(QtNetworkSettings::serverName(), 143); + socket->moveToThread(0); + QVERIFY(socket->waitForConnected(5000)); + socket->write("XXX LOGOUT\r\n"); + QVERIFY(socket->waitForBytesWritten(5000)); + QVERIFY(socket->waitForDisconnected()); + delete socket; + } + { + // Case 2: Moved before connecting + QTcpSocket *socket = newSocket(); + socket->moveToThread(0); + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket->waitForConnected(5000)); + socket->write("XXX LOGOUT\r\n"); + QVERIFY(socket->waitForBytesWritten(5000)); + QVERIFY(socket->waitForDisconnected()); + delete socket; + } + { + // Case 3: Moved after writing, while waiting for bytes to be written. + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket->waitForConnected(5000)); + socket->write("XXX LOGOUT\r\n"); + socket->moveToThread(0); + QVERIFY(socket->waitForBytesWritten(5000)); + QVERIFY(socket->waitForDisconnected()); + delete socket; + } + { + // Case 4: Moved after writing, while waiting for response. + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket->waitForConnected(5000)); + socket->write("XXX LOGOUT\r\n"); + QVERIFY(socket->waitForBytesWritten(5000)); + socket->moveToThread(0); + QVERIFY(socket->waitForDisconnected()); + delete socket; + } +} + +void tst_QTcpSocket::increaseReadBufferSize() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; //proxy not useful for localhost test case + QTcpServer server; + QTcpSocket *active = newSocket(); + connect(active, SIGNAL(readyRead()), SLOT(exitLoopSlot())); + + // connect two sockets to each other: + QVERIFY(server.listen(QHostAddress::LocalHost)); + active->connectToHost("127.0.0.1", server.serverPort()); + QVERIFY(active->waitForConnected(5000)); + QVERIFY(server.waitForNewConnection(5000)); + + QTcpSocket *passive = server.nextPendingConnection(); + QVERIFY(passive); + + // now write 512 bytes of data on one end + QByteArray data(512, 'a'); + passive->write(data); + QVERIFY2(passive->waitForBytesWritten(5000), "Network timeout"); + + // set the read buffer size to less than what was written and iterate: + active->setReadBufferSize(256); + enterLoop(10); + QVERIFY2(!timeout(), "Network timeout"); + QCOMPARE(active->bytesAvailable(), active->readBufferSize()); + + // increase the buffer size and iterate again: + active->setReadBufferSize(384); + enterLoop(10); + QVERIFY2(!timeout(), "Network timeout"); + QCOMPARE(active->bytesAvailable(), active->readBufferSize()); + + // once more, but now it should read everything there was to read + active->setReadBufferSize(1024); + enterLoop(10); + QVERIFY2(!timeout(), "Network timeout"); + QCOMPARE(active->bytesAvailable(), qint64(data.size())); + + // drain it and compare + QCOMPARE(active->readAll(), data); + + // now one more test by setting the buffer size to unlimited: + passive->write(data); + QVERIFY2(passive->waitForBytesWritten(5000), "Network timeout"); + active->setReadBufferSize(256); + enterLoop(10); + QVERIFY2(!timeout(), "Network timeout"); + QCOMPARE(active->bytesAvailable(), active->readBufferSize()); + active->setReadBufferSize(0); + enterLoop(10); + QVERIFY2(!timeout(), "Network timeout"); + QCOMPARE(active->bytesAvailable(), qint64(data.size())); + QCOMPARE(active->readAll(), data); + + delete active; +} + +void tst_QTcpSocket::taskQtBug5799ConnectionErrorWaitForConnected() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + // check that we get a proper error connecting to port 12346 + // use waitForConnected, e.g. this should use a synchronous select() on the OS level + + QTcpSocket socket; + socket.connectToHost(QtNetworkSettings::serverName(), 12346); + QTime timer; + timer.start(); + socket.waitForConnected(10000); + QVERIFY2(timer.elapsed() < 9900, "Connection to closed port timed out instead of refusing, something is wrong"); + QVERIFY2(socket.state() == QAbstractSocket::UnconnectedState, "Socket connected unexpectedly!"); + QVERIFY2(socket.error() == QAbstractSocket::ConnectionRefusedError, + QString("Could not reach server: %1").arg(socket.errorString()).toLocal8Bit()); +} + +void tst_QTcpSocket::taskQtBug5799ConnectionErrorEventLoop() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + // check that we get a proper error connecting to port 12346 + // This testcase uses an event loop + QTcpSocket socket; + connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), &QTestEventLoop::instance(), SLOT(exitLoop())); + socket.connectToHost(QtNetworkSettings::serverName(), 12346); + + QTestEventLoop::instance().enterLoop(10); + QVERIFY2(!QTestEventLoop::instance().timeout(), "Connection to closed port timed out instead of refusing, something is wrong"); + QVERIFY2(socket.state() == QAbstractSocket::UnconnectedState, "Socket connected unexpectedly!"); + QVERIFY2(socket.error() == QAbstractSocket::ConnectionRefusedError, + QString("Could not reach server: %1").arg(socket.errorString()).toLocal8Bit()); +} + +void tst_QTcpSocket::taskQtBug7054TimeoutErrorResetting() +{ + QTcpSocket *socket = newSocket(); + + socket->connectToHost(QtNetworkSettings::serverName(), 443); + QVERIFY(socket->waitForConnected(5*1000)); + QVERIFY(socket->error() == QAbstractSocket::UnknownSocketError); + + // We connected to the HTTPS port. Wait two seconds to receive data. We will receive + // nothing because we would need to start the SSL handshake + QVERIFY(!socket->waitForReadyRead(2*1000)); + QVERIFY(socket->error() == QAbstractSocket::SocketTimeoutError); + + // Now write some crap to make the server disconnect us. 4 lines are enough. + socket->write("a\r\nb\r\nc\r\nd\r\n"); + socket->waitForBytesWritten(2*1000); + + // we try to waitForReadyRead another time, but this time instead of a timeout we + // should get a better error since the server disconnected us + QVERIFY(!socket->waitForReadyRead(2*1000)); + // It must NOT be the SocketTimeoutError that had been set before + QVERIFY(socket->error() == QAbstractSocket::RemoteHostClosedError); +} + +void tst_QTcpSocket::invalidProxy_data() +{ + QTest::addColumn("type"); + QTest::addColumn("host"); + QTest::addColumn("port"); + QTest::addColumn("failsAtConnect"); + QTest::addColumn("expectedError"); + + QString fluke = QHostInfo::fromName(QtNetworkSettings::serverName()).addresses().first().toString(); + QTest::newRow("ftp-proxy") << int(QNetworkProxy::FtpCachingProxy) << fluke << 21 << true + << int(QAbstractSocket::UnsupportedSocketOperationError); + QTest::newRow("http-caching-proxy") << int(QNetworkProxy::HttpCachingProxy) << fluke << 3128 << true + << int(QAbstractSocket::UnsupportedSocketOperationError); + QTest::newRow("no-such-host-socks5") << int(QNetworkProxy::Socks5Proxy) + << "this-host-will-never-exist.troll.no" << 1080 << false + << int(QAbstractSocket::ProxyNotFoundError); + QTest::newRow("no-such-host-http") << int(QNetworkProxy::HttpProxy) + << "this-host-will-never-exist.troll.no" << 3128 << false + << int(QAbstractSocket::ProxyNotFoundError); +#if !defined(Q_OS_SYMBIAN) + QTest::newRow("http-on-socks5") << int(QNetworkProxy::HttpProxy) << fluke << 1080 << false + << int(QAbstractSocket::ProxyConnectionClosedError); + QTest::newRow("socks5-on-http") << int(QNetworkProxy::Socks5Proxy) << fluke << 3128 << false + << int(QAbstractSocket::SocketTimeoutError); +#endif +} + +void tst_QTcpSocket::invalidProxy() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + QFETCH(int, type); + QFETCH(QString, host); + QFETCH(int, port); + QFETCH(bool, failsAtConnect); + QNetworkProxy::ProxyType proxyType = QNetworkProxy::ProxyType(type); + QNetworkProxy proxy(proxyType, host, port); + + QTcpSocket *socket = newSocket(); + socket->setProxy(proxy); + socket->connectToHost(QHostInfo::fromName(QtNetworkSettings::serverName()).addresses().first().toString(), 80); + + if (failsAtConnect) { + QCOMPARE(socket->state(), QAbstractSocket::UnconnectedState); + } else { + QCOMPARE(socket->state(), QAbstractSocket::ConnectingState); + QVERIFY(!socket->waitForConnected(5000)); + } + QVERIFY(!socket->errorString().isEmpty()); + + // note: the following test is not a hard failure. + // Sometimes, error codes change for the better + QTEST(int(socket->error()), "expectedError"); + + delete socket; +} + +// copied from tst_qnetworkreply.cpp +class MyProxyFactory: public QNetworkProxyFactory +{ +public: + int callCount; + QList toReturn; + QNetworkProxyQuery lastQuery; + inline MyProxyFactory() { clear(); } + + inline void clear() + { + callCount = 0; + toReturn = QList() << QNetworkProxy::DefaultProxy; + lastQuery = QNetworkProxyQuery(); + } + + virtual QList queryProxy(const QNetworkProxyQuery &query) + { + lastQuery = query; + ++callCount; + return toReturn; + } +}; + +void tst_QTcpSocket::proxyFactory_data() +{ + QTest::addColumn >("proxyList"); + QTest::addColumn("proxyUsed"); + QTest::addColumn("failsAtConnect"); + QTest::addColumn("expectedError"); + + QList proxyList; + + // tests that do connect + + proxyList << QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3129); + QTest::newRow("http") + << proxyList << proxyList.at(0) + << false << int(QAbstractSocket::UnknownSocketError); + + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1081); + QTest::newRow("socks5") + << proxyList << proxyList.at(0) + << false << int(QAbstractSocket::UnknownSocketError); + + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129) + << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1081); + QTest::newRow("cachinghttp+socks5") + << proxyList << proxyList.at(1) + << false << int(QAbstractSocket::UnknownSocketError); + + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121) + << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129) + << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1081); + QTest::newRow("ftp+cachinghttp+socks5") + << proxyList << proxyList.at(2) + << false << int(QAbstractSocket::UnknownSocketError); + + // tests that fail to connect + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129); + QTest::newRow("cachinghttp") + << proxyList << QNetworkProxy() + << true << int(QAbstractSocket::UnsupportedSocketOperationError); + + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121); + QTest::newRow("ftp") + << proxyList << QNetworkProxy() + << true << int(QAbstractSocket::UnsupportedSocketOperationError); + + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121) + << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129); + QTest::newRow("ftp+cachinghttp") + << proxyList << QNetworkProxy() + << true << int(QAbstractSocket::UnsupportedSocketOperationError); +} + +void tst_QTcpSocket::proxyFactory() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + QFETCH(QList, proxyList); + QFETCH(QNetworkProxy, proxyUsed); + QFETCH(bool, failsAtConnect); + + MyProxyFactory *factory = new MyProxyFactory; + factory->toReturn = proxyList; + QNetworkProxyFactory::setApplicationProxyFactory(factory); + + QTcpSocket *socket = newSocket(); + QString host = QtNetworkSettings::serverName(); + socket->connectToHost(host, 80); + + // Verify that the factory was called properly + QCOMPARE(factory->callCount, 1); + QCOMPARE(factory->lastQuery, QNetworkProxyQuery(host, 80)); + + if (failsAtConnect) { + QCOMPARE(socket->state(), QAbstractSocket::UnconnectedState); + } else { + QCOMPARE(socket->state(), QAbstractSocket::ConnectingState); + QVERIFY(socket->waitForConnected(5000)); + QCOMPARE(proxyAuthCalled, 1); + } + QVERIFY(!socket->errorString().isEmpty()); + + // note: the following test is not a hard failure. + // Sometimes, error codes change for the better + QTEST(int(socket->error()), "expectedError"); + + delete socket; +} + +// there is a similar test inside tst_qtcpserver that uses the event loop instead +void tst_QTcpSocket::qtbug14268_peek() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + SocketPair socketPair; + QVERIFY(socketPair.create()); + QTcpSocket *outgoing = socketPair.endPoints[0]; + QTcpSocket *incoming = socketPair.endPoints[1]; + + QVERIFY(incoming->state() == QTcpSocket::ConnectedState); + QVERIFY(outgoing->state() == QTcpSocket::ConnectedState); + + outgoing->write("abc\n"); + QVERIFY(outgoing->waitForBytesWritten(2000)); + QVERIFY(incoming->waitForReadyRead(2000)); + QVERIFY(incoming->peek(128*1024) == QByteArray("abc\n")); + + outgoing->write("def\n"); + QVERIFY(outgoing->waitForBytesWritten(2000)); + QVERIFY(incoming->waitForReadyRead(2000)); + QVERIFY(incoming->peek(128*1024) == QByteArray("abc\ndef\n")); + + outgoing->write("ghi\n"); + QVERIFY(outgoing->waitForBytesWritten(2000)); + QVERIFY(incoming->waitForReadyRead(2000)); + QVERIFY(incoming->peek(128*1024) == QByteArray("abc\ndef\nghi\n")); + + QVERIFY(incoming->read(128*1024) == QByteArray("abc\ndef\nghi\n")); +} + + + +QTEST_MAIN(tst_QTcpSocket) +#include "tst_qtcpsocket.moc" diff --git a/tests/auto/network/socket/qudpsocket/.gitignore b/tests/auto/network/socket/qudpsocket/.gitignore new file mode 100644 index 0000000000..c6134126a0 --- /dev/null +++ b/tests/auto/network/socket/qudpsocket/.gitignore @@ -0,0 +1,2 @@ +tst_qudpsocket +clientserver/clientserver diff --git a/tests/auto/network/socket/qudpsocket/clientserver/clientserver.pro b/tests/auto/network/socket/qudpsocket/clientserver/clientserver.pro new file mode 100644 index 0000000000..6da148659c --- /dev/null +++ b/tests/auto/network/socket/qudpsocket/clientserver/clientserver.pro @@ -0,0 +1,8 @@ +QT = core network +SOURCES += main.cpp +CONFIG += console +CONFIG -= app_bundle +TARGET = clientserver +DESTDIR = ./ + +symbian: TARGET.CAPABILITY += NetworkServices diff --git a/tests/auto/network/socket/qudpsocket/clientserver/main.cpp b/tests/auto/network/socket/qudpsocket/clientserver/main.cpp new file mode 100644 index 0000000000..9145a5199f --- /dev/null +++ b/tests/auto/network/socket/qudpsocket/clientserver/main.cpp @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include + +class ClientServer : public QUdpSocket +{ + Q_OBJECT +public: + enum Type { + ConnectedClient, + UnconnectedClient, + Server + }; + + ClientServer(Type type, const QString &host, quint16 port) + : type(type) + { + switch (type) { + case Server: + if (bind(0, ShareAddress | ReuseAddressHint)) { + printf("%d\n", localPort()); + } else { + printf("XXX\n"); + } + break; + case ConnectedClient: + connectToHost(host, port); + startTimer(250); + printf("ok\n"); + break; + case UnconnectedClient: + peerAddress = host; + peerPort = port; + if (bind(QHostAddress::Any, port + 1, ShareAddress | ReuseAddressHint)) { + startTimer(250); + printf("ok\n"); + } else { + printf("XXX\n"); + } + break; + } + fflush(stdout); + + connect(this, SIGNAL(readyRead()), this, SLOT(readData())); + } + +protected: + void timerEvent(QTimerEvent *event) + { + static int n = 0; + switch (type) { + case ConnectedClient: + write(QByteArray::number(n++)); + break; + case UnconnectedClient: + writeDatagram(QByteArray::number(n++), peerAddress, peerPort); + break; + default: + break; + } + + QUdpSocket::timerEvent(event); + } + +private slots: + void readData() + { + printf("readData()\n"); + switch (type) { + case ConnectedClient: { + while (bytesAvailable() || hasPendingDatagrams()) { + QByteArray data = readAll(); + printf("got %d\n", data.toInt()); + } + break; + } + case UnconnectedClient: { + while (hasPendingDatagrams()) { + QByteArray data; + data.resize(pendingDatagramSize()); + readDatagram(data.data(), data.size()); + printf("got %d\n", data.toInt()); + } + break; + } + case Server: { + while (hasPendingDatagrams()) { + QHostAddress sender; + quint16 senderPort; + QByteArray data; + data.resize(pendingDatagramSize()); + readDatagram(data.data(), data.size(), &sender, &senderPort); + printf("got %d\n", data.toInt()); + printf("sending %d\n", data.toInt() * 2); + writeDatagram(QByteArray::number(data.toInt() * 2), sender, senderPort); + } + break; + } + } + fflush(stdout); + } + +private: + Type type; + QHostAddress peerAddress; + quint16 peerPort; +}; + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + ClientServer::Type type; + if (app.arguments().size() < 4) { + qDebug("usage: ./%s [ConnectedClient |UnconnectedClient |Server]", argv[0]); + return 1; + } + + QString arg = app.arguments().at(1).trimmed().toLower(); + if (arg == "connectedclient") + type = ClientServer::ConnectedClient; + else if (arg == "unconnectedclient") + type = ClientServer::UnconnectedClient; + else if (arg == "server") + type = ClientServer::Server; + + ClientServer clientServer(type, app.arguments().at(2), + app.arguments().at(3).toInt()); + + return app.exec(); +} + +#include "main.moc" diff --git a/tests/auto/network/socket/qudpsocket/qudpsocket.pro b/tests/auto/network/socket/qudpsocket/qudpsocket.pro new file mode 100644 index 0000000000..4ddb7178a4 --- /dev/null +++ b/tests/auto/network/socket/qudpsocket/qudpsocket.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs +SUBDIRS = test clientserver + + diff --git a/tests/auto/network/socket/qudpsocket/test/test.pro b/tests/auto/network/socket/qudpsocket/test/test.pro new file mode 100644 index 0000000000..508dc97053 --- /dev/null +++ b/tests/auto/network/socket/qudpsocket/test/test.pro @@ -0,0 +1,28 @@ +load(qttest_p4) +SOURCES += ../tst_qudpsocket.cpp +QT = core network + +MOC_DIR=tmp + +win32 { + CONFIG(debug, debug|release) { + DESTDIR = ../debug +} else { + DESTDIR = ../release + } +} else { + DESTDIR = ../ +} + +wince*|symbian: { + addApp.files = ../clientserver/clientserver.exe + addApp.path = clientserver + DEPLOYMENT += addApp +} + +TARGET = tst_qudpsocket + +symbian: TARGET.CAPABILITY += NetworkServices + + +CONFIG+=insignificant_test diff --git a/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp b/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp new file mode 100644 index 0000000000..83d30cc40a --- /dev/null +++ b/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp @@ -0,0 +1,1356 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "../../../network-settings.h" + +#ifndef QT_NO_BEARERMANAGEMENT +#include +#include +#include +#endif + +Q_DECLARE_METATYPE(QHostAddress) +Q_DECLARE_METATYPE(QNetworkInterface) +Q_DECLARE_METATYPE(QSharedPointer) +//TESTED_CLASS= +//TESTED_FILES= + +QT_FORWARD_DECLARE_CLASS(QUdpSocket) + +class tst_QUdpSocket : public QObject +{ + Q_OBJECT + +public: + tst_QUdpSocket(); + virtual ~tst_QUdpSocket(); + + +public slots: + void initTestCase_data(); + void init(); + void cleanup(); +private slots: + void constructing(); + void unconnectedServerAndClientTest(); + void broadcasting(); + void loop_data(); + void loop(); + void ipv6Loop_data(); + void ipv6Loop(); + void dualStack(); + void readLine(); + void pendingDatagramSize(); + void writeDatagram(); + void performance(); + void bindMode(); + void writeDatagramToNonExistingPeer_data(); + void writeDatagramToNonExistingPeer(); + void writeToNonExistingPeer_data(); + void writeToNonExistingPeer(); + void outOfProcessConnectedClientServerTest(); + void outOfProcessUnconnectedClientServerTest(); + void zeroLengthDatagram(); + void multicastTtlOption_data(); + void multicastTtlOption(); + void multicastLoopbackOption_data(); + void multicastLoopbackOption(); + void multicastJoinBeforeBind_data(); + void multicastJoinBeforeBind(); + void multicastLeaveAfterClose_data(); + void multicastLeaveAfterClose(); + void setMulticastInterface_data(); + void setMulticastInterface(); + void multicast_data(); + void multicast(); + void echo_data(); + void echo(); + +protected slots: + void empty_readyReadSlot(); + void empty_connectedSlot(); + +private: +#ifndef QT_NO_BEARERMANAGEMENT + QNetworkConfigurationManager *netConfMan; + QNetworkConfiguration networkConfiguration; + QSharedPointer networkSession; +#endif +}; + +tst_QUdpSocket::tst_QUdpSocket() +{ + Q_SET_DEFAULT_IAP +} + +tst_QUdpSocket::~tst_QUdpSocket() +{ +} + +void tst_QUdpSocket::initTestCase_data() +{ + QTest::addColumn("setProxy"); + QTest::addColumn("proxyType"); + + QTest::newRow("WithoutProxy") << false << 0; + QTest::newRow("WithSocks5Proxy") << true << int(QNetworkProxy::Socks5Proxy); + +#ifndef QT_NO_BEARERMANAGEMENT + netConfMan = new QNetworkConfigurationManager(this); + networkConfiguration = netConfMan->defaultConfiguration(); + networkSession = QSharedPointer(new QNetworkSession(networkConfiguration)); + if (!networkSession->isOpen()) { + networkSession->open(); + QVERIFY(networkSession->waitForOpened(30000)); + } +#endif +} + +void tst_QUdpSocket::init() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) { + QFETCH_GLOBAL(int, proxyType); + if (proxyType == QNetworkProxy::Socks5Proxy) { + QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080)); + } + } +} + +void tst_QUdpSocket::cleanup() +{ + QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy); +} + + +//---------------------------------------------------------------------------------- + +void tst_QUdpSocket::constructing() +{ + QUdpSocket socket; +#ifdef FORCE_SESSION + socket.setProperty("_q_networksession", QVariant::fromValue(networkSession)); +#endif + + QVERIFY(socket.isSequential()); + QVERIFY(!socket.isOpen()); + QVERIFY(socket.socketType() == QUdpSocket::UdpSocket); + QCOMPARE((int) socket.bytesAvailable(), 0); + QCOMPARE(socket.canReadLine(), false); + QCOMPARE(socket.readLine(), QByteArray()); + QCOMPARE(socket.socketDescriptor(), -1); + QCOMPARE(socket.error(), QUdpSocket::UnknownSocketError); + QCOMPARE(socket.errorString(), QString("Unknown error")); + + // Check the state of the socket api +} + +void tst_QUdpSocket::unconnectedServerAndClientTest() +{ + QUdpSocket serverSocket; +#ifdef FORCE_SESSION + serverSocket.setProperty("_q_networksession", QVariant::fromValue(networkSession)); +#endif + + qRegisterMetaType("QAbstractSocket::SocketState"); + + QSignalSpy stateChangedSpy(&serverSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState))); + QVERIFY2(serverSocket.bind(), serverSocket.errorString().toLatin1().constData()); + QCOMPARE(stateChangedSpy.count(), 1); + + const char *message[] = {"Yo mista", "Yo", "Wassap"}; + + QHostAddress serverAddress = QHostAddress::LocalHost; + if (!(serverSocket.localAddress() == QHostAddress::AnyIPv4 || serverSocket.localAddress() == QHostAddress::AnyIPv6)) + serverAddress = serverSocket.localAddress(); + + for (int i = 0; i < 3; ++i) { + QUdpSocket clientSocket; +#ifdef FORCE_SESSION + clientSocket.setProperty("_q_networksession", QVariant::fromValue(networkSession)); +#endif + QCOMPARE(int(clientSocket.writeDatagram(message[i], strlen(message[i]), + serverAddress, serverSocket.localPort())), + int(strlen(message[i]))); + char buf[1024]; + QHostAddress host; + quint16 port; + QVERIFY(serverSocket.waitForReadyRead(5000)); + QCOMPARE(int(serverSocket.readDatagram(buf, sizeof(buf), &host, &port)), + int(strlen(message[i]))); + buf[strlen(message[i])] = '\0'; + QCOMPARE(QByteArray(buf), QByteArray(message[i])); + } +} + +//---------------------------------------------------------------------------------- + +void tst_QUdpSocket::broadcasting() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) { + QFETCH_GLOBAL(int, proxyType); + if (proxyType == QNetworkProxy::Socks5Proxy) { + QSKIP("With socks5 Broadcast is not supported.", SkipSingle); + } + } +#ifdef Q_OS_AIX + QSKIP("Broadcast does not work on darko", SkipAll); +#endif + const char *message[] = {"Yo mista", "", "Yo", "Wassap"}; + + QList broadcastAddresses; + foreach (QNetworkInterface iface, QNetworkInterface::allInterfaces()) { + if ((iface.flags() & QNetworkInterface::CanBroadcast) + && iface.flags() & QNetworkInterface::IsUp) { + for (int i=0;i("peterMessage"); + QTest::addColumn("paulMessage"); + QTest::addColumn("success"); + + QTest::newRow("\"Almond!\" | \"Joy!\"") << QByteArray("Almond!") << QByteArray("Joy!") << true; + QTest::newRow("\"A\" | \"B\"") << QByteArray("A") << QByteArray("B") << true; + QTest::newRow("\"AB\" | \"B\"") << QByteArray("AB") << QByteArray("B") << true; + QTest::newRow("\"AB\" | \"BB\"") << QByteArray("AB") << QByteArray("BB") << true; + QTest::newRow("\"A\\0B\" | \"B\\0B\"") << QByteArray::fromRawData("A\0B", 3) << QByteArray::fromRawData("B\0B", 3) << true; + QTest::newRow("\"(nil)\" | \"(nil)\"") << QByteArray() << QByteArray() << true; + QTest::newRow("Bigmessage") << QByteArray(600, '@') << QByteArray(600, '@') << true; +} + +void tst_QUdpSocket::loop() +{ + QFETCH(QByteArray, peterMessage); + QFETCH(QByteArray, paulMessage); + QFETCH(bool, success); + + QUdpSocket peter; + QUdpSocket paul; +#ifdef FORCE_SESSION + peter.setProperty("_q_networksession", QVariant::fromValue(networkSession)); + paul.setProperty("_q_networksession", QVariant::fromValue(networkSession)); +#endif + + QVERIFY2(peter.bind(), peter.errorString().toLatin1().constData()); + QVERIFY2(paul.bind(), paul.errorString().toLatin1().constData()); + + QHostAddress peterAddress = QHostAddress::LocalHost; + if (!(peter.localAddress() == QHostAddress::AnyIPv4 || peter.localAddress() == QHostAddress::AnyIPv6)) + peterAddress = peter.localAddress(); + QHostAddress pualAddress = QHostAddress::LocalHost; + if (!(paul.localAddress() == QHostAddress::AnyIPv4 || paul.localAddress() == QHostAddress::AnyIPv6)) + pualAddress = paul.localAddress(); + + QCOMPARE(peter.writeDatagram(peterMessage.data(), peterMessage.length(), + pualAddress, paul.localPort()), qint64(peterMessage.length())); + QCOMPARE(paul.writeDatagram(paulMessage.data(), paulMessage.length(), + peterAddress, peter.localPort()), qint64(paulMessage.length())); + + QVERIFY(peter.waitForReadyRead(9000)); + QVERIFY(paul.waitForReadyRead(9000)); + char peterBuffer[16*1024]; + char paulBuffer[16*1024]; + if (success) { + QCOMPARE(peter.readDatagram(peterBuffer, sizeof(peterBuffer)), qint64(paulMessage.length())); + QCOMPARE(paul.readDatagram(paulBuffer, sizeof(peterBuffer)), qint64(peterMessage.length())); + } else { + QVERIFY(peter.readDatagram(peterBuffer, sizeof(peterBuffer)) != paulMessage.length()); + QVERIFY(paul.readDatagram(paulBuffer, sizeof(peterBuffer)) != peterMessage.length()); + } + + QCOMPARE(QByteArray(peterBuffer, paulMessage.length()), paulMessage); + QCOMPARE(QByteArray(paulBuffer, peterMessage.length()), peterMessage); +} + +//---------------------------------------------------------------------------------- + +void tst_QUdpSocket::ipv6Loop_data() +{ + loop_data(); +} + +void tst_QUdpSocket::ipv6Loop() +{ + QFETCH(QByteArray, peterMessage); + QFETCH(QByteArray, paulMessage); + QFETCH(bool, success); + + QUdpSocket peter; + QUdpSocket paul; +#ifdef FORCE_SESSION + peter.setProperty("_q_networksession", QVariant::fromValue(networkSession)); + paul.setProperty("_q_networksession", QVariant::fromValue(networkSession)); +#endif + + quint16 peterPort = 28124; + quint16 paulPort = 28123; + + if (!peter.bind(QHostAddress::LocalHostIPv6, peterPort)) { + QCOMPARE(peter.error(), QUdpSocket::UnsupportedSocketOperationError); + } else { + QVERIFY(paul.bind(QHostAddress::LocalHostIPv6, paulPort)); + + QCOMPARE(peter.writeDatagram(peterMessage.data(), peterMessage.length(), QHostAddress("::1"), + paulPort), qint64(peterMessage.length())); + QCOMPARE(paul.writeDatagram(paulMessage.data(), paulMessage.length(), + QHostAddress("::1"), peterPort), qint64(paulMessage.length())); + + char peterBuffer[16*1024]; + char paulBuffer[16*1024]; +#if !defined(Q_OS_WINCE) + QVERIFY(peter.waitForReadyRead(5000)); + QVERIFY(paul.waitForReadyRead(5000)); +#else + QVERIFY(peter.waitForReadyRead(15000)); + QVERIFY(paul.waitForReadyRead(15000)); +#endif + if (success) { + QCOMPARE(peter.readDatagram(peterBuffer, sizeof(peterBuffer)), qint64(paulMessage.length())); + QCOMPARE(paul.readDatagram(paulBuffer, sizeof(peterBuffer)), qint64(peterMessage.length())); + } else { + QVERIFY(peter.readDatagram(peterBuffer, sizeof(peterBuffer)) != paulMessage.length()); + QVERIFY(paul.readDatagram(paulBuffer, sizeof(peterBuffer)) != peterMessage.length()); + } + + QCOMPARE(QByteArray(peterBuffer, paulMessage.length()), paulMessage); + QCOMPARE(QByteArray(paulBuffer, peterMessage.length()), peterMessage); + } +} + +void tst_QUdpSocket::dualStack() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + QSKIP("test server SOCKS proxy doesn't support IPv6", SkipSingle); + QUdpSocket dualSock; + QByteArray dualData("dual"); + QVERIFY(dualSock.bind(QHostAddress(QHostAddress::Any), 0)); + + QUdpSocket v4Sock; + QByteArray v4Data("v4"); + QVERIFY(v4Sock.bind(QHostAddress(QHostAddress::AnyIPv4), 0)); + + QUdpSocket v6Sock; + QByteArray v6Data("v6"); + QVERIFY(v6Sock.bind(QHostAddress(QHostAddress::AnyIPv6), 0)); + + QHostAddress from; + quint16 port; + QByteArray buffer; + //test v4 -> dual + QCOMPARE((int)v4Sock.writeDatagram(v4Data.constData(), v4Data.length(), QHostAddress(QHostAddress::LocalHost), dualSock.localPort()), v4Data.length()); + QVERIFY(dualSock.waitForReadyRead(5000)); + buffer.reserve(100); + qint64 size = dualSock.readDatagram(buffer.data(), 100, &from, &port); + QCOMPARE((int)size, v4Data.length()); + buffer.resize(size); + QCOMPARE(buffer, v4Data); + + //test v6 -> dual + QCOMPARE((int)v6Sock.writeDatagram(v6Data.constData(), v6Data.length(), QHostAddress(QHostAddress::LocalHostIPv6), dualSock.localPort()), v6Data.length()); + QVERIFY(dualSock.waitForReadyRead(5000)); + buffer.reserve(100); + size = dualSock.readDatagram(buffer.data(), 100, &from, &port); + QCOMPARE((int)size, v6Data.length()); + buffer.resize(size); + QCOMPARE(buffer, v6Data); + + //test dual -> v4 + QCOMPARE((int)dualSock.writeDatagram(dualData.constData(), dualData.length(), QHostAddress(QHostAddress::LocalHost), v4Sock.localPort()), dualData.length()); + QVERIFY(v4Sock.waitForReadyRead(5000)); + buffer.reserve(100); + size = v4Sock.readDatagram(buffer.data(), 100, &from, &port); + QCOMPARE((int)size, dualData.length()); + buffer.resize(size); + QCOMPARE(buffer, dualData); + + //test dual -> v6 + QCOMPARE((int)dualSock.writeDatagram(dualData.constData(), dualData.length(), QHostAddress(QHostAddress::LocalHostIPv6), v6Sock.localPort()), dualData.length()); + QVERIFY(v6Sock.waitForReadyRead(5000)); + buffer.reserve(100); + size = v6Sock.readDatagram(buffer.data(), 100, &from, &port); + QCOMPARE((int)size, dualData.length()); + buffer.resize(size); + QCOMPARE(buffer, dualData); + +} + +void tst_QUdpSocket::empty_readyReadSlot() +{ + QTestEventLoop::instance().exitLoop(); +} + +void tst_QUdpSocket::empty_connectedSlot() +{ + QTestEventLoop::instance().exitLoop(); +} + +//---------------------------------------------------------------------------------- + +void tst_QUdpSocket::readLine() +{ + QUdpSocket socket1; + QUdpSocket socket2; +#ifdef FORCE_SESSION + socket1.setProperty("_q_networksession", QVariant::fromValue(networkSession)); + socket2.setProperty("_q_networksession", QVariant::fromValue(networkSession)); +#endif + QVERIFY2(socket1.bind(), socket1.errorString().toLatin1().constData()); + + socket2.connectToHost("127.0.0.1", socket1.localPort()); + QVERIFY(socket2.waitForConnected(5000)); +} + +//---------------------------------------------------------------------------------- + +void tst_QUdpSocket::pendingDatagramSize() +{ + QUdpSocket server; +#ifdef FORCE_SESSION + server.setProperty("_q_networksession", QVariant::fromValue(networkSession)); +#endif + QVERIFY2(server.bind(), server.errorString().toLatin1().constData()); + + QHostAddress serverAddress = QHostAddress::LocalHost; + if (!(server.localAddress() == QHostAddress::AnyIPv4 || server.localAddress() == QHostAddress::AnyIPv6)) + serverAddress = server.localAddress(); + + QUdpSocket client; +#ifdef FORCE_SESSION + client.setProperty("_q_networksession", QVariant::fromValue(networkSession)); +#endif + QVERIFY(client.writeDatagram("this is", 7, serverAddress, server.localPort()) == 7); + QVERIFY(client.writeDatagram(0, 0, serverAddress, server.localPort()) == 0); + QVERIFY(client.writeDatagram("3 messages", 10, serverAddress, server.localPort()) == 10); + + char c = 0; + QVERIFY(server.waitForReadyRead()); + if (server.hasPendingDatagrams()) { +#if defined Q_OS_HPUX && defined __ia64 + QEXPECT_FAIL("", "HP-UX 11i v2 can't determine the datagram size correctly.", Abort); +#endif + QCOMPARE(server.pendingDatagramSize(), qint64(7)); + c = '\0'; + QCOMPARE(server.readDatagram(&c, 1), qint64(1)); + QCOMPARE(c, 't'); + c = '\0'; + } else { + QSKIP("does not have the 1st datagram", SkipSingle); + } + + if (server.hasPendingDatagrams()) { + QCOMPARE(server.pendingDatagramSize(), qint64(0)); + QCOMPARE(server.readDatagram(&c, 1), qint64(0)); + QCOMPARE(c, '\0'); // untouched + c = '\0'; + } else { + QSKIP("does not have the 2nd datagram", SkipSingle); + } + + if (server.hasPendingDatagrams()) { + QCOMPARE(server.pendingDatagramSize(), qint64(10)); + QCOMPARE(server.readDatagram(&c, 1), qint64(1)); + QCOMPARE(c, '3'); + } else { + QSKIP("does not have the 3rd datagram", SkipSingle); + } +} + + +void tst_QUdpSocket::writeDatagram() +{ + QUdpSocket server; +#ifdef FORCE_SESSION + server.setProperty("_q_networksession", QVariant::fromValue(networkSession)); +#endif + QVERIFY2(server.bind(), server.errorString().toLatin1().constData()); + + QHostAddress serverAddress = QHostAddress::LocalHost; + if (!(server.localAddress() == QHostAddress::AnyIPv4 || server.localAddress() == QHostAddress::AnyIPv6)) + serverAddress = server.localAddress(); + + QUdpSocket client; +#ifdef FORCE_SESSION + client.setProperty("_q_networksession", QVariant::fromValue(networkSession)); +#endif + + qRegisterMetaType("qint64"); + qRegisterMetaType("QAbstractSocket::SocketError"); + + for(int i=0;;i++) { + QSignalSpy errorspy(&client, SIGNAL(error(QAbstractSocket::SocketError))); + QSignalSpy bytesspy(&client, SIGNAL(bytesWritten(qint64))); + + qint64 written = client.writeDatagram(QByteArray(i * 1024, 'w'), serverAddress, + server.localPort()); + + if (written != i * 1024) { +#if defined (Q_OS_HPUX) + QSKIP("HP-UX 11.11 on hai (PA-RISC 64) truncates too long datagrams.", SkipSingle); +#endif + QCOMPARE(bytesspy.count(), 0); + QCOMPARE(errorspy.count(), 1); + QCOMPARE(*static_cast(errorspy.at(0).at(0).constData()), + int(QUdpSocket::DatagramTooLargeError)); + QCOMPARE(client.error(), QUdpSocket::DatagramTooLargeError); + break; + } + QVERIFY(bytesspy.count() == 1); + QCOMPARE(*static_cast(bytesspy.at(0).at(0).constData()), + qint64(i * 1024)); + QCOMPARE(errorspy.count(), 0); + if (!server.waitForReadyRead(5000)) { +#ifdef Q_OS_SYMBIAN + //symbian receive buffer for datagrams is ~30k, but it can send datagrams up to the maximum 64k... + if (i > 28) { + i = 64; + continue; + } +#endif + QSKIP(QString("UDP packet lost at size %1, unable to complete the test.").arg(i * 1024).toLatin1().data(), SkipSingle); + } + QCOMPARE(server.pendingDatagramSize(), qint64(i * 1024)); + QCOMPARE(server.readDatagram(0, 0), qint64(0)); + } +} + +void tst_QUdpSocket::performance() +{ +#if defined(Q_OS_SYMBIAN) + // Large packets seems not to go through on Symbian + // Reason might be also fragmentation due to VPN connection etc + + QFETCH_GLOBAL(bool, setProxy); + QFETCH_GLOBAL(int, proxyType); + + int arrSize = 8192; + if (setProxy && proxyType == QNetworkProxy::Socks5Proxy) + arrSize = 1024; + + QByteArray arr(arrSize, '@'); +#else + QByteArray arr(8192, '@'); +#endif // Q_OS_SYMBIAN + + QUdpSocket server; +#ifdef FORCE_SESSION + server.setProperty("_q_networksession", QVariant::fromValue(networkSession)); +#endif + QVERIFY2(server.bind(), server.errorString().toLatin1().constData()); + + QHostAddress serverAddress = QHostAddress::LocalHost; + if (!(server.localAddress() == QHostAddress::AnyIPv4 || server.localAddress() == QHostAddress::AnyIPv6)) + serverAddress = server.localAddress(); + + QUdpSocket client; +#ifdef FORCE_SESSION + client.setProperty("_q_networksession", QVariant::fromValue(networkSession)); +#endif + client.connectToHost(serverAddress, server.localPort()); + QVERIFY(client.waitForConnected(10000)); + + QTime stopWatch; + stopWatch.start(); + + qint64 nbytes = 0; + while (stopWatch.elapsed() < 5000) { + for (int i = 0; i < 100; ++i) { + if (client.write(arr.data(), arr.size()) > 0) { + do { + nbytes += server.readDatagram(arr.data(), arr.size()); + } while (server.hasPendingDatagrams()); + } + } + } + + float secs = stopWatch.elapsed() / 1000.0; + qDebug("\t%.2fMB/%.2fs: %.2fMB/s", float(nbytes / (1024.0*1024.0)), + secs, float(nbytes / (1024.0*1024.0)) / secs); + +#if defined(Q_OS_SYMBIAN) + if(nbytes == 0) { + qDebug("No bytes passed through local UDP socket, since UDP socket write returns EWOULDBLOCK"); + qDebug("Should try with blocking sockets, but it is not currently possible due to Open C defect"); + } +#endif + +} + +void tst_QUdpSocket::bindMode() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) { + QFETCH_GLOBAL(int, proxyType); + if (proxyType == QNetworkProxy::Socks5Proxy) { + QSKIP("With socks5 explicit port binding is not supported.", SkipAll); + } + } + + QUdpSocket socket; +#ifdef FORCE_SESSION + socket.setProperty("_q_networksession", QVariant::fromValue(networkSession)); +#endif + QVERIFY2(socket.bind(), socket.errorString().toLatin1().constData()); + QUdpSocket socket2; +#ifdef FORCE_SESSION + socket2.setProperty("_q_networksession", QVariant::fromValue(networkSession)); +#endif + QVERIFY(!socket2.bind(socket.localPort())); +#if defined(Q_OS_SYMBIAN) + if(RProcess().HasCapability(ECapabilityNetworkControl)) { + qDebug("Test executed *with* NetworkControl capability"); + // In Symbian OS ReuseAddressHint together with NetworkControl capability + // gives application *always* right to bind to port. I.e. it does not matter + // if first socket was bound with any bind flag. Since autotests in Symbian + // are currently executed with ALL -TCB rights, this path is the one executed. + QVERIFY(socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint)); + socket.close(); + socket2.close(); + + QVERIFY2(socket.bind(0, QUdpSocket::ShareAddress), socket.errorString().toLatin1().constData()); + QVERIFY(!socket2.bind(socket.localPort())); + QVERIFY2(socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint), socket2.errorString().toLatin1().constData()); + socket.close(); + socket2.close(); + + QVERIFY2(socket.bind(0, QUdpSocket::DontShareAddress), socket.errorString().toLatin1().constData()); + QVERIFY(!socket2.bind(socket.localPort())); + QVERIFY(socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint)); + socket.close(); + socket2.close(); + } else { + qDebug("Test executed *without* NetworkControl capability"); + // If we don't have NetworkControl capability, attempt to bind already bound + // address will *always* fail. I.e. it does not matter if first socket was + // bound with any bind flag. + QVERIFY(!socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint)); + socket.close(); + + QVERIFY2(socket.bind(0, QUdpSocket::ShareAddress), socket.errorString().toLatin1().constData()); + QVERIFY(!socket2.bind(socket.localPort())); + QVERIFY2(!socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint), socket2.errorString().toLatin1().constData()); + socket.close(); + + QVERIFY2(socket.bind(0, QUdpSocket::DontShareAddress), socket.errorString().toLatin1().constData()); + QVERIFY(!socket2.bind(socket.localPort())); + QVERIFY(!socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint)); + socket.close(); + } +#elif defined(Q_OS_UNIX) + QVERIFY(!socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint)); + socket.close(); + QVERIFY2(socket.bind(0, QUdpSocket::ShareAddress), socket.errorString().toLatin1().constData()); + QVERIFY2(socket2.bind(socket.localPort()), socket2.errorString().toLatin1().constData()); + socket2.close(); + QVERIFY2(socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint), socket2.errorString().toLatin1().constData()); +#else + + // Depending on the user's privileges, this or will succeed or + // fail. Admins are allowed to reuse the address, but nobody else. + if (!socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint), socket2.errorString().toLatin1().constData()) + qWarning("Failed to bind with QUdpSocket::ReuseAddressHint, user isn't an administrator?"); + socket.close(); + QVERIFY2(socket.bind(0, QUdpSocket::ShareAddress), socket.errorString().toLatin1().constData()); + QVERIFY(!socket2.bind(socket.localPort())); + socket.close(); + QVERIFY2(socket.bind(0, QUdpSocket::DontShareAddress), socket.errorString().toLatin1().constData()); + QVERIFY(!socket2.bind(socket.localPort())); + QVERIFY(!socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint)); +#endif +} + +void tst_QUdpSocket::writeDatagramToNonExistingPeer_data() +{ + QTest::addColumn("bind"); + QTest::addColumn("peerAddress"); + QHostAddress localhost(QHostAddress::LocalHost); + QHostAddress remote = QHostInfo::fromName(QtNetworkSettings::serverName()).addresses().first(); + + QTest::newRow("localhost-unbound") << false << localhost; + QTest::newRow("localhost-bound") << true << localhost; + QTest::newRow("remote-unbound") << false << remote; + QTest::newRow("remote-bound") << true << remote; +} + +void tst_QUdpSocket::writeDatagramToNonExistingPeer() +{ + QFETCH(bool, bind); + QFETCH(QHostAddress, peerAddress); + + quint16 peerPort = 33533 + int(bind); + + QUdpSocket sUdp; +#ifdef FORCE_SESSION + sUdp.setProperty("_q_networksession", QVariant::fromValue(networkSession)); +#endif + QSignalSpy sReadyReadSpy(&sUdp, SIGNAL(readyRead())); + if (bind) + QVERIFY(sUdp.bind()); + QCOMPARE(sUdp.writeDatagram("", 1, peerAddress, peerPort), qint64(1)); + QTestEventLoop::instance().enterLoop(1); + QCOMPARE(sReadyReadSpy.count(), 0); +} + +void tst_QUdpSocket::writeToNonExistingPeer_data() +{ + QTest::addColumn("peerAddress"); + QHostAddress localhost(QHostAddress::LocalHost); + QHostAddress remote = QHostInfo::fromName(QtNetworkSettings::serverName()).addresses().first(); + // write (required to be connected) + QTest::newRow("localhost") << localhost; + QTest::newRow("remote") << remote; +} + +void tst_QUdpSocket::writeToNonExistingPeer() +{ + QSKIP("Connected-mode UDP sockets and their behaviour are erratic", SkipAll); + QFETCH(QHostAddress, peerAddress); + quint16 peerPort = 34534; + qRegisterMetaType("QAbstractSocket::SocketError"); + + QUdpSocket sConnected; +#ifdef FORCE_SESSION + sConnected.setProperty("_q_networksession", QVariant::fromValue(networkSession)); +#endif + QSignalSpy sConnectedReadyReadSpy(&sConnected, SIGNAL(readyRead())); + QSignalSpy sConnectedErrorSpy(&sConnected, SIGNAL(error(QAbstractSocket::SocketError))); + sConnected.connectToHost(peerAddress, peerPort, QIODevice::ReadWrite); + QVERIFY(sConnected.waitForConnected(10000)); + + // the first write succeeds... + QCOMPARE(sConnected.write("", 1), qint64(1)); + + // the second one should fail! + QTest::qSleep(1000); // do not process events + QCOMPARE(sConnected.write("", 1), qint64(-1)); + QCOMPARE(int(sConnected.error()), int(QUdpSocket::ConnectionRefusedError)); + + // the third one will succeed... + QCOMPARE(sConnected.write("", 1), qint64(1)); + QTestEventLoop::instance().enterLoop(1); + QCOMPARE(sConnectedReadyReadSpy.count(), 0); + QCOMPARE(sConnectedErrorSpy.count(), 1); + QCOMPARE(int(sConnected.error()), int(QUdpSocket::ConnectionRefusedError)); + + // we should now get a read error + QCOMPARE(sConnected.write("", 1), qint64(1)); + QTest::qSleep(1000); // do not process events + char buf[2]; + QVERIFY(!sConnected.hasPendingDatagrams()); + QCOMPARE(sConnected.bytesAvailable(), Q_INT64_C(0)); + QCOMPARE(sConnected.pendingDatagramSize(), Q_INT64_C(-1)); + QCOMPARE(sConnected.readDatagram(buf, 2), Q_INT64_C(-1)); + QCOMPARE(int(sConnected.error()), int(QUdpSocket::ConnectionRefusedError)); + + QCOMPARE(sConnected.write("", 1), qint64(1)); + QTest::qSleep(1000); // do not process events + QCOMPARE(sConnected.read(buf, 2), Q_INT64_C(0)); + QCOMPARE(int(sConnected.error()), int(QUdpSocket::ConnectionRefusedError)); + + // we should still be connected + QCOMPARE(int(sConnected.state()), int(QUdpSocket::ConnectedState)); +} + +void tst_QUdpSocket::outOfProcessConnectedClientServerTest() +{ +#if defined(Q_OS_WINCE) || defined (Q_OS_SYMBIAN) + QSKIP("This test depends on reading data from QProcess (not supported on Qt/WinCE and Symbian).", SkipAll); +#endif +#if defined(QT_NO_PROCESS) + QSKIP("Qt was compiled with QT_NO_PROCESS", SkipAll); +#else + + QProcess serverProcess; + serverProcess.start(QLatin1String("clientserver/clientserver server 1 1"), + QIODevice::ReadWrite | QIODevice::Text); + QVERIFY2(serverProcess.waitForStarted(3000), + qPrintable("Failed to start subprocess: " + serverProcess.errorString())); + + // Wait until the server has started and reports success. + while (!serverProcess.canReadLine()) + QVERIFY(serverProcess.waitForReadyRead(3000)); + QByteArray serverGreeting = serverProcess.readLine(); + QVERIFY(serverGreeting != QByteArray("XXX\n")); + int serverPort = serverGreeting.trimmed().toInt(); + QVERIFY(serverPort > 0 && serverPort < 65536); + + QProcess clientProcess; + clientProcess.start(QString::fromLatin1("clientserver/clientserver connectedclient %1 %2") + .arg(QLatin1String("127.0.0.1")).arg(serverPort), + QIODevice::ReadWrite | QIODevice::Text); + QVERIFY2(clientProcess.waitForStarted(3000), + qPrintable("Failed to start subprocess: " + clientProcess.errorString())); + + // Wait until the server has started and reports success. + while (!clientProcess.canReadLine()) + QVERIFY(clientProcess.waitForReadyRead(3000)); + QByteArray clientGreeting = clientProcess.readLine(); + QCOMPARE(clientGreeting, QByteArray("ok\n")); + + // Let the client and server talk for 3 seconds + QTest::qWait(3000); + + QStringList serverData = QString::fromLocal8Bit(serverProcess.readAll()).split("\n"); + QStringList clientData = QString::fromLocal8Bit(clientProcess.readAll()).split("\n"); + QVERIFY(serverData.size() > 5); + QVERIFY(clientData.size() > 5); + + for (int i = 0; i < clientData.size() / 2; ++i) { + QCOMPARE(clientData.at(i * 2), QString("readData()")); + QCOMPARE(serverData.at(i * 3), QString("readData()")); + + QString cdata = clientData.at(i * 2 + 1); + QString sdata = serverData.at(i * 3 + 1); + QVERIFY(cdata.startsWith(QLatin1String("got "))); + + QCOMPARE(cdata.mid(4).trimmed().toInt(), sdata.mid(4).trimmed().toInt() * 2); + QVERIFY(serverData.at(i * 3 + 2).startsWith(QLatin1String("sending "))); + QCOMPARE(serverData.at(i * 3 + 2).trimmed().mid(8).toInt(), + sdata.mid(4).trimmed().toInt() * 2); + } + + clientProcess.kill(); + QVERIFY(clientProcess.waitForFinished()); + serverProcess.kill(); + QVERIFY(serverProcess.waitForFinished()); +#endif +} + +void tst_QUdpSocket::outOfProcessUnconnectedClientServerTest() +{ +#if defined(Q_OS_WINCE) || defined (Q_OS_SYMBIAN) + QSKIP("This test depends on reading data from QProcess (not supported on Qt/WinCE and Symbian).", SkipAll); +#endif +#if defined(QT_NO_PROCESS) + QSKIP("Qt was compiled with QT_NO_PROCESS", SkipAll); +#else + + QProcess serverProcess; + serverProcess.start(QLatin1String("clientserver/clientserver server 1 1"), + QIODevice::ReadWrite | QIODevice::Text); + QVERIFY2(serverProcess.waitForStarted(3000), + qPrintable("Failed to start subprocess: " + serverProcess.errorString())); + + // Wait until the server has started and reports success. + while (!serverProcess.canReadLine()) + QVERIFY(serverProcess.waitForReadyRead(3000)); + QByteArray serverGreeting = serverProcess.readLine(); + QVERIFY(serverGreeting != QByteArray("XXX\n")); + int serverPort = serverGreeting.trimmed().toInt(); + QVERIFY(serverPort > 0 && serverPort < 65536); + + QProcess clientProcess; + clientProcess.start(QString::fromLatin1("clientserver/clientserver unconnectedclient %1 %2") + .arg(QLatin1String("127.0.0.1")).arg(serverPort), + QIODevice::ReadWrite | QIODevice::Text); + QVERIFY2(clientProcess.waitForStarted(3000), + qPrintable("Failed to start subprocess: " + clientProcess.errorString())); + + // Wait until the server has started and reports success. + while (!clientProcess.canReadLine()) + QVERIFY(clientProcess.waitForReadyRead(3000)); + QByteArray clientGreeting = clientProcess.readLine(); + QCOMPARE(clientGreeting, QByteArray("ok\n")); + + // Let the client and server talk for 3 seconds + QTest::qWait(3000); + + QStringList serverData = QString::fromLocal8Bit(serverProcess.readAll()).split("\n"); + QStringList clientData = QString::fromLocal8Bit(clientProcess.readAll()).split("\n"); + + QVERIFY(serverData.size() > 5); + QVERIFY(clientData.size() > 5); + + for (int i = 0; i < clientData.size() / 2; ++i) { + QCOMPARE(clientData.at(i * 2), QString("readData()")); + QCOMPARE(serverData.at(i * 3), QString("readData()")); + + QString cdata = clientData.at(i * 2 + 1); + QString sdata = serverData.at(i * 3 + 1); + QVERIFY(cdata.startsWith(QLatin1String("got "))); + + QCOMPARE(cdata.mid(4).trimmed().toInt(), sdata.mid(4).trimmed().toInt() * 2); + QVERIFY(serverData.at(i * 3 + 2).startsWith(QLatin1String("sending "))); + QCOMPARE(serverData.at(i * 3 + 2).trimmed().mid(8).toInt(), + sdata.mid(4).trimmed().toInt() * 2); + } + + clientProcess.kill(); + QVERIFY(clientProcess.waitForFinished()); + serverProcess.kill(); + QVERIFY(serverProcess.waitForFinished()); +#endif +} + +void tst_QUdpSocket::zeroLengthDatagram() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + QUdpSocket receiver; +#ifdef FORCE_SESSION + receiver.setProperty("_q_networksession", QVariant::fromValue(networkSession)); +#endif + QVERIFY(receiver.bind()); + + QVERIFY(!receiver.waitForReadyRead(100)); + QVERIFY(!receiver.hasPendingDatagrams()); + + QUdpSocket sender; +#ifdef FORCE_SESSION + sender.setProperty("_q_networksession", QVariant::fromValue(networkSession)); +#endif + QCOMPARE(sender.writeDatagram(QByteArray(), QHostAddress::LocalHost, receiver.localPort()), qint64(0)); + + QVERIFY(receiver.waitForReadyRead(1000)); + QVERIFY(receiver.hasPendingDatagrams()); + + char buf; + QCOMPARE(receiver.readDatagram(&buf, 1), qint64(0)); +} + +void tst_QUdpSocket::multicastTtlOption_data() +{ + QTest::addColumn("bindAddress"); + QTest::addColumn("ttl"); + QTest::addColumn("expected"); + + QList addresses; + addresses += QHostAddress(QHostAddress::Any); + addresses += QHostAddress(QHostAddress::AnyIPv6); + + foreach (const QHostAddress &address, addresses) { + QTest::newRow(QString("%1 0").arg(address.toString()).toAscii()) << address << 0 << 0; + QTest::newRow(QString("%1 1").arg(address.toString()).toAscii()) << address << 1 << 1; + QTest::newRow(QString("%1 2").arg(address.toString()).toAscii()) << address << 2 << 2; + QTest::newRow(QString("%1 128").arg(address.toString()).toAscii()) << address << 128 << 128; + QTest::newRow(QString("%1 255").arg(address.toString()).toAscii()) << address << 255 << 255; + QTest::newRow(QString("%1 1024").arg(address.toString()).toAscii()) << address << 1024 << 1; + } +} + +void tst_QUdpSocket::multicastTtlOption() +{ + QFETCH_GLOBAL(bool, setProxy); + QFETCH(QHostAddress, bindAddress); + QFETCH(int, ttl); + QFETCH(int, expected); + if (setProxy) { + // UDP multicast does not work with proxies + expected = 0; + } + + QUdpSocket udpSocket; +#ifdef FORCE_SESSION + udpSocket.setProperty("_q_networksession", QVariant::fromValue(networkSession)); +#endif + // bind, but ignore the result, we are only interested in initializing the socket + (void) udpSocket.bind(bindAddress, 0); + udpSocket.setSocketOption(QUdpSocket::MulticastTtlOption, ttl); + QCOMPARE(udpSocket.socketOption(QUdpSocket::MulticastTtlOption).toInt(), expected); +} + +void tst_QUdpSocket::multicastLoopbackOption_data() +{ + QTest::addColumn("bindAddress"); + QTest::addColumn("loopback"); + QTest::addColumn("expected"); + + QList addresses; + addresses += QHostAddress(QHostAddress::Any); + addresses += QHostAddress(QHostAddress::AnyIPv6); + + foreach (const QHostAddress &address, addresses) { + QTest::newRow(QString("%1 0").arg(address.toString()).toAscii()) << address << 0 << 0; + QTest::newRow(QString("%1 1").arg(address.toString()).toAscii()) << address << 1 << 1; + QTest::newRow(QString("%1 2").arg(address.toString()).toAscii()) << address << 2 << 1; + QTest::newRow(QString("%1 0 again").arg(address.toString()).toAscii()) << address << 0 << 0; + QTest::newRow(QString("%1 2 again").arg(address.toString()).toAscii()) << address << 2 << 1; + QTest::newRow(QString("%1 0 last time").arg(address.toString()).toAscii()) << address << 0 << 0; + QTest::newRow(QString("%1 1 again").arg(address.toString()).toAscii()) << address << 1 << 1; + } +} + +void tst_QUdpSocket::multicastLoopbackOption() +{ + QFETCH_GLOBAL(bool, setProxy); + QFETCH(QHostAddress, bindAddress); + QFETCH(int, loopback); + QFETCH(int, expected); + if (setProxy) { + // UDP multicast does not work with proxies + expected = 0; + } + + QUdpSocket udpSocket; +#ifdef FORCE_SESSION + udpSocket.setProperty("_q_networksession", QVariant::fromValue(networkSession)); +#endif + // bind, but ignore the result, we are only interested in initializing the socket + (void) udpSocket.bind(bindAddress, 0); + udpSocket.setSocketOption(QUdpSocket::MulticastLoopbackOption, loopback); + QCOMPARE(udpSocket.socketOption(QUdpSocket::MulticastLoopbackOption).toInt(), expected); +} + +void tst_QUdpSocket::multicastJoinBeforeBind_data() +{ + QTest::addColumn("groupAddress"); + QTest::newRow("valid ipv4 group address") << QHostAddress("239.255.118.62"); + QTest::newRow("invalid ipv4 group address") << QHostAddress(QHostAddress::Broadcast); + QTest::newRow("valid ipv6 group address") << QHostAddress("FF01::114"); + QTest::newRow("invalid ipv6 group address") << QHostAddress(QHostAddress::AnyIPv6); +} + +void tst_QUdpSocket::multicastJoinBeforeBind() +{ + QFETCH(QHostAddress, groupAddress); + + QUdpSocket udpSocket; +#ifdef FORCE_SESSION + udpSocket.setProperty("_q_networksession", QVariant::fromValue(networkSession)); +#endif + // cannot join group before binding + QTest::ignoreMessage(QtWarningMsg, "QUdpSocket::joinMulticastGroup() called on a QUdpSocket when not in QUdpSocket::BoundState"); + QVERIFY(!udpSocket.joinMulticastGroup(groupAddress)); +} + +void tst_QUdpSocket::multicastLeaveAfterClose_data() +{ + QTest::addColumn("groupAddress"); + QTest::newRow("valid ipv4 group address") << QHostAddress("239.255.118.62"); + QTest::newRow("valid ipv6 group address") << QHostAddress("FF01::114"); +} + +void tst_QUdpSocket::multicastLeaveAfterClose() +{ + QFETCH_GLOBAL(bool, setProxy); + QFETCH(QHostAddress, groupAddress); + if (setProxy) { + QSKIP("UDP Multicast does not work with proxies", SkipAll); + } + + QUdpSocket udpSocket; +#ifdef FORCE_SESSION + udpSocket.setProperty("_q_networksession", QVariant::fromValue(networkSession)); +#endif +#ifdef Q_OS_SYMBIAN + QVERIFY2(udpSocket.bind(), + qPrintable(udpSocket.errorString())); +#else + QVERIFY2(udpSocket.bind(groupAddress, 0), + qPrintable(udpSocket.errorString())); +#endif + QVERIFY2(udpSocket.joinMulticastGroup(groupAddress), + qPrintable(udpSocket.errorString())); + udpSocket.close(); + QTest::ignoreMessage(QtWarningMsg, "QUdpSocket::leaveMulticastGroup() called on a QUdpSocket when not in QUdpSocket::BoundState"); + QVERIFY(!udpSocket.leaveMulticastGroup(groupAddress)); +} + +void tst_QUdpSocket::setMulticastInterface_data() +{ + QTest::addColumn("iface"); + QTest::addColumn("address"); + QList interfaces = QNetworkInterface::allInterfaces(); + foreach (const QNetworkInterface &iface, interfaces) { + foreach (const QNetworkAddressEntry &entry, iface.addressEntries()) { + QTest::newRow(QString("%1:%2").arg(iface.name()).arg(entry.ip().toString()).toAscii()) + << iface + << entry.ip(); + } + } +} + +void tst_QUdpSocket::setMulticastInterface() +{ +#ifdef Q_OS_SYMBIAN + QSKIP("Symbian has no IPV6_MULTICAST_IF equivalent", SkipAll); +#else + QFETCH_GLOBAL(bool, setProxy); + QFETCH(QNetworkInterface, iface); + QFETCH(QHostAddress, address); + + QUdpSocket udpSocket; + // bind initializes the socket + bool bound = udpSocket.bind((address.protocol() == QAbstractSocket::IPv6Protocol + ? QHostAddress(QHostAddress::AnyIPv6) + : QHostAddress(QHostAddress::Any)), + 0); + if (!bound) + QTest::ignoreMessage(QtWarningMsg, "QUdpSocket::setMulticastInterface() called on a QUdpSocket when not in QUdpSocket::BoundState"); + udpSocket.setMulticastInterface(iface); + if (!bound) + QTest::ignoreMessage(QtWarningMsg, "QUdpSocket::multicastInterface() called on a QUdpSocket when not in QUdpSocket::BoundState"); + QNetworkInterface iface2 = udpSocket.multicastInterface(); + if (!setProxy) { + QVERIFY(iface2.isValid()); + QCOMPARE(iface.name(), iface2.name()); + } else { + QVERIFY(!iface2.isValid()); + } +#endif +} + +void tst_QUdpSocket::multicast_data() +{ + QHostAddress anyAddress = QHostAddress(QHostAddress::AnyIPv4); + QHostAddress groupAddress = QHostAddress("239.255.118.62"); + QHostAddress any6Address = QHostAddress(QHostAddress::AnyIPv6); + QHostAddress group6Address = QHostAddress("FF01::114"); + + QTest::addColumn("bindAddress"); + QTest::addColumn("bindResult"); + QTest::addColumn("groupAddress"); + QTest::addColumn("joinResult"); + QTest::newRow("valid bind, group ipv4 address") << anyAddress << true << groupAddress << true; + QTest::newRow("valid bind, invalid group ipv4 address") << anyAddress << true << anyAddress << false; + QTest::newRow("same bind, group ipv4 address") << groupAddress << true << groupAddress << true; + QTest::newRow("valid bind, group ipv6 address") << any6Address << true << group6Address << true; + QTest::newRow("valid bind, invalid group ipv6 address") << any6Address << true << any6Address << false; + QTest::newRow("same bind, group ipv6 address") << group6Address << true << group6Address << true; +} + +void tst_QUdpSocket::multicast() +{ + QFETCH_GLOBAL(bool, setProxy); + QFETCH(QHostAddress, bindAddress); + QFETCH(bool, bindResult); + QFETCH(QHostAddress, groupAddress); + QFETCH(bool, joinResult); + if (setProxy) { + // UDP multicast does not work with proxies + if ( +#ifndef Q_OS_WIN + //windows native socket engine binds 0.0.0.0 instead of the requested multicast address + (bindAddress.protocol() == QAbstractSocket::IPv4Protocol && (bindAddress.toIPv4Address() & 0xffff0000) == 0xefff0000) || +#endif + bindAddress.protocol() == QAbstractSocket::IPv6Protocol) { + // proxy cannot bind to IPv6 or multicast addresses + bindResult = false; + } + joinResult = false; + } + + QUdpSocket receiver; +#ifdef FORCE_SESSION + receiver.setProperty("_q_networksession", QVariant::fromValue(networkSession)); +#endif + + // bind first, then verify that we can join the multicast group +#ifdef Q_OS_SYMBIAN + if (!setProxy) { + QEXPECT_FAIL("same bind, group ipv4 address", "bind to group address not supported on symbian", Abort); + QEXPECT_FAIL("same bind, group ipv6 address", "bind to group address not supported on symbian", Abort); + } +#endif + QVERIFY2(receiver.bind(bindAddress, 0) == bindResult, + qPrintable(receiver.errorString())); + if (!bindResult) + return; + + QVERIFY2(receiver.joinMulticastGroup(groupAddress) == joinResult, + qPrintable(receiver.errorString())); + if (!joinResult) + return; + + QList datagrams = QList() + << QByteArray("0123") + << QByteArray("4567") + << QByteArray("89ab") + << QByteArray("cdef"); + + QUdpSocket sender; +#ifdef FORCE_SESSION + sender.setProperty("_q_networksession", QVariant::fromValue(networkSession)); +#endif + sender.bind(); + foreach (const QByteArray &datagram, datagrams) { + QCOMPARE(int(sender.writeDatagram(datagram, groupAddress, receiver.localPort())), + int(datagram.size())); + } + + QVERIFY2(receiver.waitForReadyRead(), + qPrintable(receiver.errorString())); + QVERIFY(receiver.hasPendingDatagrams()); + QList receivedDatagrams; + while (receiver.hasPendingDatagrams()) { + QByteArray datagram; + datagram.resize(receiver.pendingDatagramSize()); + receiver.readDatagram(datagram.data(), datagram.size(), 0, 0); + receivedDatagrams << datagram; + } +#ifdef Q_OS_SYMBIAN + QEXPECT_FAIL("valid bind, group ipv4 address", "IPv4 multicast not supported on symbian", Abort); +#endif + QCOMPARE(receivedDatagrams, datagrams); + + QVERIFY2(receiver.leaveMulticastGroup(groupAddress), qPrintable(receiver.errorString())); +} + +void tst_QUdpSocket::echo_data() +{ + QTest::addColumn("connect"); + QTest::newRow("writeDatagram") << false; + QTest::newRow("write") << true; +} + +void tst_QUdpSocket::echo() +{ + QFETCH(bool, connect); + QHostInfo info = QHostInfo::fromName(QtNetworkSettings::serverName()); + QVERIFY(info.addresses().count()); + QHostAddress remote = info.addresses().first(); + + QUdpSocket sock; +#ifdef FORCE_SESSION + sock.setProperty("_q_networksession", QVariant::fromValue(networkSession)); +#endif + if (connect) { + sock.connectToHost(remote, 7); + QVERIFY(sock.waitForConnected(10000)); + } else { + sock.bind(); + } + QByteArray out(30, 'x'); + QByteArray in; + int successes = 0; + for (int i=0;i<10;i++) { + if (connect) { + sock.write(out); + } else { + sock.writeDatagram(out, remote, 7); + } + if (sock.waitForReadyRead(1000)) { + while (sock.hasPendingDatagrams()) { + QHostAddress from; + quint16 port; + if (connect) { + in = sock.read(sock.pendingDatagramSize()); + } else { + in.resize(sock.pendingDatagramSize()); + sock.readDatagram(in.data(), in.length(), &from, &port); + } + if (in==out) + successes++; + } + } + if (!sock.isValid()) + QFAIL(sock.errorString().toLatin1().constData()); + qDebug() << "packets in" << successes << "out" << i; + QTest::qWait(50); //choke to avoid triggering flood/DDoS protections on echo service + } + QVERIFY(successes >= 9); +} + +QTEST_MAIN(tst_QUdpSocket) +#include "tst_qudpsocket.moc" diff --git a/tests/auto/network/socket/qudpsocket/udpServer/main.cpp b/tests/auto/network/socket/qudpsocket/udpServer/main.cpp new file mode 100644 index 0000000000..ff3ca111d0 --- /dev/null +++ b/tests/auto/network/socket/qudpsocket/udpServer/main.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include + +class Server : public QObject +{ + Q_OBJECT +public: + Server(int port) + { + connect(&serverSocket, SIGNAL(readyRead()), + this, SLOT(sendEcho())); + if (serverSocket.bind(QHostAddress::Any, port, + QUdpSocket::ReuseAddressHint + | QUdpSocket::ShareAddress)) { + printf("OK\n"); + } else { + printf("FAILED\n"); + } + fflush(stdout); + } + +private slots: + void sendEcho() + { + QHostAddress senderAddress; + quint16 senderPort; + + char data[1024]; + qint64 bytes = serverSocket.readDatagram(data, sizeof(data), &senderAddress, &senderPort); + if (bytes == 1 && data[0] == '\0') + QCoreApplication::instance()->quit(); + + for (int i = 0; i < bytes; ++i) + data[i] += 1; + serverSocket.writeDatagram(data, bytes, senderAddress, senderPort); + } + +private: + QUdpSocket serverSocket; +}; + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + + Server server(app.arguments().at(1).toInt()); + + return app.exec(); +} + +#include "main.moc" diff --git a/tests/auto/network/socket/qudpsocket/udpServer/udpServer.pro b/tests/auto/network/socket/qudpsocket/udpServer/udpServer.pro new file mode 100644 index 0000000000..7438d40fb0 --- /dev/null +++ b/tests/auto/network/socket/qudpsocket/udpServer/udpServer.pro @@ -0,0 +1,7 @@ +SOURCES += main.cpp +QT = core network +CONFIG -= app_bundle +CONFIG += console + +symbian:TARGET.CAPABILITY="ALL -TCB" + diff --git a/tests/auto/network/socket/socket.pro b/tests/auto/network/socket/socket.pro new file mode 100644 index 0000000000..49fb52d1a5 --- /dev/null +++ b/tests/auto/network/socket/socket.pro @@ -0,0 +1,17 @@ +TEMPLATE=subdirs +SUBDIRS=\ + qhttpsocketengine \ + qudpsocket \ + qtcpsocket \ + #qlocalsocket \ # FIXME: uses qtscript (QTBUG-19242) + qtcpserver \ + qsocks5socketengine \ + qabstractsocket \ + platformsocketengine \ + +!contains(QT_CONFIG, private_tests): SUBDIRS -= \ + platformsocketengine \ + qhttpsocketengine \ + qsocks5socketengine \ + + diff --git a/tests/auto/network/ssl/qsslcertificate/.gitignore b/tests/auto/network/ssl/qsslcertificate/.gitignore new file mode 100644 index 0000000000..25b34756b7 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/.gitignore @@ -0,0 +1 @@ +tst_qsslcertificate diff --git a/tests/auto/network/ssl/qsslcertificate/certificates/ca-cert.pem b/tests/auto/network/ssl/qsslcertificate/certificates/ca-cert.pem new file mode 100644 index 0000000000..bcba68aefa --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/certificates/ca-cert.pem @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIC5TCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQQFADBcMQswCQYDVQQGEwJBVTET +MBEGA1UECBMKUXVlZW5zbGFuZDEaMBgGA1UEChMRQ3J5cHRTb2Z0IFB0eSBMdGQx +HDAaBgNVBAMTE1Rlc3QgUENBICgxMDI0IGJpdCkwHhcNOTkxMjAyMjEzODUxWhcN +MDUwNzEwMjEzODUxWjBbMQswCQYDVQQGEwJBVTETMBEGA1UECBMKUXVlZW5zbGFu +ZDEaMBgGA1UEChMRQ3J5cHRTb2Z0IFB0eSBMdGQxGzAZBgNVBAMTElRlc3QgQ0Eg +KDEwMjQgYml0KTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAo7ujy3XXpU/p +yDJtOxkMJmGv3mdiVm7JrdoKLUgqjO2rBaeNuYMUiuI6oYU+tlD6agwRML0Pn2JF +b90VdK/UXrmRr9djaEuH17EIKjte5RwOzndCndsjcCYyoeODMTyg7dqPIkDMmRNM +5R5xBTabD+Aji0wzQupYxBLuW5PLj7ECAwEAAaOBtzCBtDAdBgNVHQ4EFgQU1WWA +U42mkhi3ecgey1dsJjU61+UwgYQGA1UdIwR9MHuAFE0RaEcrj18q1dw+G6nJbsTW +R213oWCkXjBcMQswCQYDVQQGEwJBVTETMBEGA1UECBMKUXVlZW5zbGFuZDEaMBgG +A1UEChMRQ3J5cHRTb2Z0IFB0eSBMdGQxHDAaBgNVBAMTE1Rlc3QgUENBICgxMDI0 +IGJpdCmCAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQBb39BRphHL +6aRAQyymsvBvPSCiG9+kR0R1L23aTpNbhXp2BebyFjbEQYZc2kWGiKKcHkNECA35 +3d4LoqUlVey8DFyafOIJd9hxdZfg+rxlHMxnL7uCJRmx9+xB411Jtsol9/wg1uCK +sleGpgB4j8cG2SVCz7V2MNZNK+d5QCnR7A== +-----END CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQCju6PLddelT+nIMm07GQwmYa/eZ2JWbsmt2gotSCqM7asFp425 +gxSK4jqhhT62UPpqDBEwvQ+fYkVv3RV0r9ReuZGv12NoS4fXsQgqO17lHA7Od0Kd +2yNwJjKh44MxPKDt2o8iQMyZE0zlHnEFNpsP4COLTDNC6ljEEu5bk8uPsQIDAQAB +AoGAVZmpFZsDZfr0l2S9tLLwpjRWNOlKATQkno6q2WesT0eGLQufTciY+c8ypfU6 +hyio8r5iUl/VhhdjhAtKx1mRpiotftHo/eYf8rtsrnprOnWG0bWjLjtIoMbcxGn2 +J3bN6LJmbJMjDs0eJ3KnTu646F3nDUw2oGAwmpzKXA1KAP0CQQDRvQhxk2D3Pehs +HvG665u2pB5ipYQngEFlZO7RHJZzJOZEWSLuuMqaF/7pTfA5jiBvWqCgJeCRRInL +21ru4dlPAkEAx9jj7BgKn5TYnMoBSSe0afjsV9oApVpN1Nacb1YDtCwy+scp3++s +nFxlv98wxIlSdpwMUn+AUWfjiWR7Tu/G/wJBAJ/KjwZIrFVxewP0x2ILYsTRYLzz +MS4PDsO7FB+I0i7DbBOifXS2oNSpd3I0CNMwrxFnUHzynpbOStVfN3ZL5w0CQQCa +pwFahxBRhkJKsxhjoFJBX9yl75JoY4Wvm5Tbo9ih6UJaRx3kqfkN14L2BKYcsZgb +KY9vmDOYy6iNfjDeWTfJAkBkfPUb8oTJ/nSP5zN6sqGxSY4krc4xLxpRmxoJ8HL2 +XfhqXkTzbU13RX9JJ/NZ8vQN9Vm2NhxRGJocQkmcdVtJ +-----END RSA PRIVATE KEY----- diff --git a/tests/auto/network/ssl/qsslcertificate/certificates/ca-cert.pem.digest-md5 b/tests/auto/network/ssl/qsslcertificate/certificates/ca-cert.pem.digest-md5 new file mode 100644 index 0000000000..800a05b030 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/certificates/ca-cert.pem.digest-md5 @@ -0,0 +1 @@ +MD5 Fingerprint=EF:02:83:EA:AC:AF:6A:D0:8D:4F:56:A8:2B:A1:C5:D3 diff --git a/tests/auto/network/ssl/qsslcertificate/certificates/ca-cert.pem.digest-sha1 b/tests/auto/network/ssl/qsslcertificate/certificates/ca-cert.pem.digest-sha1 new file mode 100644 index 0000000000..df311a8dcb --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/certificates/ca-cert.pem.digest-sha1 @@ -0,0 +1 @@ +SHA1 Fingerprint=A6:CC:2A:D7:E3:8F:49:E7:8B:4F:76:E8:E0:FA:37:5E:62:2F:66:23 diff --git a/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss-san-utf8.pem b/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss-san-utf8.pem new file mode 100644 index 0000000000..e1b731d69b --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss-san-utf8.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICkTCCAfqgAwIBAgIJAL1nF+PLAF2KMA0GCSqGSIb3DQEBBQUAMGkxKzApBgNV +BAoMIkjElcSCxrLDvyDKjeG6v8qI4bq34bi7IFLDqWPDtnJkxZ0xFTATBgNVBAsM +DOOIp0HjiYHvvatCQzEWMBQGA1UEAwwNSm9obm55IEd1aXRhcjELMAkGA1UEBhMC +Tk8wHhcNMTEwNTA1MDgxMzEwWhcNMTEwNjA0MDgxMzEwWjBpMSswKQYDVQQKDCJI +xJXEgsayw78gyo3hur/KiOG6t+G4uyBSw6ljw7ZyZMWdMRUwEwYDVQQLDAzjiKdB +44mB772rQkMxFjAUBgNVBAMMDUpvaG5ueSBHdWl0YXIxCzAJBgNVBAYTAk5PMIGf +MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC2zSxS17I6596dJE/VAmGz+06D9S8n +3C0hnIGNVu+LwbgDJTvOw0SzNj4UP72UGgd3UI1KLBg5XWIsRNmE3COJMMh6syjI +L1Ept+tVXxGL6n4gl+0nZ7dkUyxJmeFtigYrL+qCH1yd5rmf3sC3jO4IosuAiG66 +IDkJEVo64NT8ZQIDAQABo0EwPzA9BgNVHREENjA0gQ9hcm5lQGZvb2Jhci5vcmeC +Dnd3dy5mb29iYXIub3JngRFiamFybmVAZm9vYmFyLm9yZzANBgkqhkiG9w0BAQUF +AAOBgQAqVhbC0/EUFdnKlYV3PrknwGX1dPEPGJuIQHa0KpoicvNiOhs1HxBDYbzc +F6wcAMEynq4YwGKhcQLZOs2mo0LreAjA9rU/yBnqrnUW/4gxtUUvmJKK+62IjfLp +eO1L+1NcEMJiaZf8fip4VXhXdOYUhgE8WUZ1UJRC6w3T/yAgcQ== +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss-san-utf8.pem.san b/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss-san-utf8.pem.san new file mode 100644 index 0000000000..f46a637da4 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss-san-utf8.pem.san @@ -0,0 +1,5 @@ +[subj_alt_name] +subjectAltName=\ + email:arne@foobar.org,\ + DNS:www.foobar.org,\ + email:bjarne@foobar.org diff --git a/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss-san.pem b/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss-san.pem new file mode 100644 index 0000000000..3d0bdfcee8 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss-san.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB9zCCAWCgAwIBAgIJAIyyZjJFdeyIMA0GCSqGSIb3DQEBBQUAMBwxGjAYBgNV +BAMTEUpvaG5ueSBHdWl0YXJDPU5PMB4XDTA3MDQyMDEwMzIzOVoXDTA3MDUyMDEw +MzIzOVowHDEaMBgGA1UEAxMRSm9obm55IEd1aXRhckM9Tk8wgZ8wDQYJKoZIhvcN +AQEBBQADgY0AMIGJAoGBANPgV/8gMg5Gh4vhfcoUTHXTqhcEuCh5VE/h57Ea7uj4 +1/bQtUNUvRZO21KtAmLHkFLoNQqeYbFJ4ZP7u/R/WDNk76EQtYNcMmJgSu/QRlxj +bEFFBOPPflQH7nYdneMegszzijRQ25oZhnjbyI0xZgqpNZwipBkC5lPgsrmlOckd +AgMBAAGjQTA/MD0GA1UdEQQ2MDSBD2FybmVAZm9vYmFyLm9yZ4IOd3d3LmZvb2Jh +ci5vcmeBEWJqYXJuZUBmb29iYXIub3JnMA0GCSqGSIb3DQEBBQUAA4GBAFVqCnFr +5EevQiVtAbDlTSbTJ3XWJSzjU0yf+tNYvPEIEqoDVh25YhSNWqRCMYFiUomj55WY +Rf7C4JM/eRlo99xnR4OtJzfLi+q1eKhl53cuwooajRjVOxQsdHpke51L9UzibKGw +0o8D/FNBw+D4GwIC1sdKw2UWAeaMhNzSEWKA +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss-san.pem.san b/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss-san.pem.san new file mode 100644 index 0000000000..f46a637da4 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss-san.pem.san @@ -0,0 +1,5 @@ +[subj_alt_name] +subjectAltName=\ + email:arne@foobar.org,\ + DNS:www.foobar.org,\ + email:bjarne@foobar.org diff --git a/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.der b/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.der new file mode 100644 index 0000000000..ea9eedc643 Binary files /dev/null and b/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.der differ diff --git a/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.der.pubkey b/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.der.pubkey new file mode 100644 index 0000000000..48d2b99c3a Binary files /dev/null and b/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.der.pubkey differ diff --git a/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.pem b/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.pem new file mode 100644 index 0000000000..b2626f3d1a --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIByTCCATICCQDiwxj6Xps7zDANBgkqhkiG9w0BAQUFADApMRowGAYDVQQDExFu +YW1lL3dpdGgvc2xhc2hlczELMAkGA1UEBhMCTk8wHhcNMDcwNDE3MDc0MDI2WhcN +MDcwNTE3MDc0MDI2WjApMRowGAYDVQQDExFuYW1lL3dpdGgvc2xhc2hlczELMAkG +A1UEBhMCTk8wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOud6QOsME+pWANE +xxgmL0iT1ayg++hTxHsqAYnm/FoMxfUh+NdKkgJn2/GfNppinfPOSI667VqonU+7 +JBZDTLV5CPbZIo9fFQpDJQN6naev4yaxU1VeYFfI7S8c8zYKeGSR+RenNNeLvfH8 +0YxPpZZ1snv8IfDH2V8MVxiyr7lLAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAT9Kz +0kg75nMzcg23J0q6OGvkj0VO6QOaAwqt6v+Kp5kgOzUEisbVt9oxZrWwkEw6dFNN +cl7Blq6HQm6beezJobhEsi3G8OjVBrTDFb+jqPi/kwXuKL3QyfY2r/LRuINGrBDi +Ewg7KuI0qZPe0bQ9NCM5TCD6qCj4vx3HdIHU4Ew= +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.pem.digest-md5 b/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.pem.digest-md5 new file mode 100644 index 0000000000..62f4d76072 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.pem.digest-md5 @@ -0,0 +1 @@ +MD5 Fingerprint=F4:2B:9D:73:2B:C8:26:56:60:9C:7F:58:66:07:4A:46 diff --git a/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.pem.digest-sha1 b/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.pem.digest-sha1 new file mode 100644 index 0000000000..02430ed89b --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.pem.digest-sha1 @@ -0,0 +1 @@ +SHA1 Fingerprint=B5:CF:31:AE:89:FB:BA:20:31:89:BA:71:06:7C:D7:84:9D:39:9E:46 diff --git a/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.pem.pubkey b/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.pem.pubkey new file mode 100644 index 0000000000..5344d112cb --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.pem.pubkey @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDrnekDrDBPqVgDRMcYJi9Ik9Ws +oPvoU8R7KgGJ5vxaDMX1IfjXSpICZ9vxnzaaYp3zzkiOuu1aqJ1PuyQWQ0y1eQj2 +2SKPXxUKQyUDep2nr+MmsVNVXmBXyO0vHPM2CnhkkfkXpzTXi73x/NGMT6WWdbJ7 +/CHwx9lfDFcYsq+5SwIDAQAB +-----END PUBLIC KEY----- diff --git a/tests/auto/network/ssl/qsslcertificate/certificates/cert.der b/tests/auto/network/ssl/qsslcertificate/certificates/cert.der new file mode 100644 index 0000000000..aeb8571817 Binary files /dev/null and b/tests/auto/network/ssl/qsslcertificate/certificates/cert.der differ diff --git a/tests/auto/network/ssl/qsslcertificate/certificates/cert.der.pubkey b/tests/auto/network/ssl/qsslcertificate/certificates/cert.der.pubkey new file mode 100644 index 0000000000..48d2b99c3a Binary files /dev/null and b/tests/auto/network/ssl/qsslcertificate/certificates/cert.der.pubkey differ diff --git a/tests/auto/network/ssl/qsslcertificate/certificates/cert.pem b/tests/auto/network/ssl/qsslcertificate/certificates/cert.pem new file mode 100644 index 0000000000..295010c210 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/certificates/cert.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB8zCCAVwCAREwDQYJKoZIhvcNAQEFBQAwWzELMAkGA1UEBhMCQVUxEzARBgNV +BAgTClF1ZWVuc2xhbmQxGjAYBgNVBAoTEUNyeXB0U29mdCBQdHkgTHRkMRswGQYD +VQQDExJUZXN0IENBICgxMDI0IGJpdCkwHhcNMDcwNDE3MDc0MDI2WhcNMDcwNTE3 +MDc0MDI2WjApMRowGAYDVQQDExFuYW1lL3dpdGgvc2xhc2hlczELMAkGA1UEBhMC +Tk8wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOud6QOsME+pWANExxgmL0iT +1ayg++hTxHsqAYnm/FoMxfUh+NdKkgJn2/GfNppinfPOSI667VqonU+7JBZDTLV5 +CPbZIo9fFQpDJQN6naev4yaxU1VeYFfI7S8c8zYKeGSR+RenNNeLvfH80YxPpZZ1 +snv8IfDH2V8MVxiyr7lLAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAleaU4pgzV6KY ++q9QuXapUYMsC2GiNtDmkG3k+MTHUO8XlE4hqPrIM6rRf7zKQdZ950R2wL9FSnYl +Qm1Tdv38dCka6ivMBqvRuOt9axH3m0G7nzHL7U3zaCbtEx3yVln+b3yYtiVpTuq0 +3MLrt7tQGAW6ra8ISf6YY1W65/uVXZE= +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslcertificate/certificates/cert.pem.digest-md5 b/tests/auto/network/ssl/qsslcertificate/certificates/cert.pem.digest-md5 new file mode 100644 index 0000000000..5333f63f06 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/certificates/cert.pem.digest-md5 @@ -0,0 +1 @@ +MD5 Fingerprint=B6:CF:57:34:DA:A9:73:21:82:F7:CF:4D:3D:85:31:88 diff --git a/tests/auto/network/ssl/qsslcertificate/certificates/cert.pem.digest-sha1 b/tests/auto/network/ssl/qsslcertificate/certificates/cert.pem.digest-sha1 new file mode 100644 index 0000000000..62f84deb96 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/certificates/cert.pem.digest-sha1 @@ -0,0 +1 @@ +SHA1 Fingerprint=B6:D1:51:82:E0:29:CA:59:96:38:BD:B6:F9:40:05:91:6D:49:09:60 diff --git a/tests/auto/network/ssl/qsslcertificate/certificates/cert.pem.pubkey b/tests/auto/network/ssl/qsslcertificate/certificates/cert.pem.pubkey new file mode 100644 index 0000000000..5344d112cb --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/certificates/cert.pem.pubkey @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDrnekDrDBPqVgDRMcYJi9Ik9Ws +oPvoU8R7KgGJ5vxaDMX1IfjXSpICZ9vxnzaaYp3zzkiOuu1aqJ1PuyQWQ0y1eQj2 +2SKPXxUKQyUDep2nr+MmsVNVXmBXyO0vHPM2CnhkkfkXpzTXi73x/NGMT6WWdbJ7 +/CHwx9lfDFcYsq+5SwIDAQAB +-----END PUBLIC KEY----- diff --git a/tests/auto/network/ssl/qsslcertificate/certificates/gencertificates.sh b/tests/auto/network/ssl/qsslcertificate/certificates/gencertificates.sh new file mode 100755 index 0000000000..0bac191326 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/certificates/gencertificates.sh @@ -0,0 +1,104 @@ +#!/bin/sh +############################################################################# +## +## Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +## All rights reserved. +## Contact: Nokia Corporation (qt-info@nokia.com) +## +## This file is the build configuration utility of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:LGPL$ +## GNU Lesser General Public License Usage +## This file may be used under the terms of the GNU Lesser General Public +## License version 2.1 as published by the Free Software Foundation and +## appearing in the file LICENSE.LGPL included in the packaging of this +## file. Please review the following information to ensure the GNU Lesser +## General Public License version 2.1 requirements will be met: +## http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +## +## In addition, as a special exception, Nokia gives you certain additional +## rights. These rights are described in the Nokia Qt LGPL Exception +## version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU General +## Public License version 3.0 as published by the Free Software Foundation +## and appearing in the file LICENSE.GPL included in the packaging of this +## file. Please review the following information to ensure the GNU General +## Public License version 3.0 requirements will be met: +## http://www.gnu.org/copyleft/gpl.html. +## +## Other Usage +## Alternatively, this file may be used in accordance with the terms and +## conditions contained in a signed written agreement between you and Nokia. +## +## +## +## +## +## $QT_END_LICENSE$ +## +############################################################################# + +# This script generates digital certificates of different types. + +#--- Certificates --------------------------------------------------------------------------- +echo -e "\ngenerating 1024-bit RSA private key to PEM file ..." +openssl genrsa -out rsa-pri-1024.pem 1024 + +echo -e "\ngenerating the corresponding public key to PEM and DER file ..." +openssl rsa -in rsa-pri-1024.pem -pubout -out rsa-pub-1024.pem +openssl rsa -in rsa-pri-1024.pem -pubout -out rsa-pub-1024.der -outform der + +echo -e "\ngenerating certificate signing request (CSR) ..." +openssl req -out req.pem -new -key rsa-pri-1024.pem -subj "/CN=name\/with\/slashes/C=NO" + +echo -e "\n generating a self-signed certifificate to PEM file ..." +openssl x509 -req -in req.pem -out cert-ss.pem -signkey rsa-pri-1024.pem + +echo -e "\n generating a self-signed certifificate to DER file ..." +openssl x509 -req -in req.pem -out cert-ss.der -signkey rsa-pri-1024.pem -outform der + +echo -e "\n generating a certifificate signed by a dummy CA to PEM file ..." +openssl x509 -req -in req.pem -out cert.pem -CA ca-cert.pem -set_serial 17 + +echo -e "\n generating a certifificate signed by a dummy CA to DER file ..." +openssl x509 -req -in req.pem -out cert.der -CA ca-cert.pem -set_serial 17 -outform der + +#--- Public keys -------------------------------------------------------------------------------- +echo -e "\n associate public keys with all certificates ..." +# Note: For now, there is only one public key (encoded in both PEM and DER), but that could change. +/bin/cp rsa-pub-1024.pem cert-ss.pem.pubkey +/bin/cp rsa-pub-1024.der cert-ss.der.pubkey +/bin/cp rsa-pub-1024.pem cert.pem.pubkey +/bin/cp rsa-pub-1024.der cert.der.pubkey + +#--- Digests -------------------------------------------------------------------------------- +echo -e "\n generating md5 and sha1 digests of all certificates ..." +for digest in md5 sha1 +do + openssl x509 -in ca-cert.pem -noout -fingerprint -$digest > ca-cert.pem.digest-$digest + openssl x509 -in cert-ss.pem -noout -fingerprint -$digest > cert-ss.pem.digest-$digest + openssl x509 -in cert.pem -noout -fingerprint -$digest > cert.pem.digest-$digest +done + +#--- Subjet Alternative Name extension ---------------------------------------------------- +echo -e "\n generating self signed root cert. with Subject Alternative Name extension (X509v3) ..." +outname=cert-ss-san.pem +openssl req -out req-san.pem -new -key rsa-pri-1024.pem -subj "/CN=Johnny GuitarC=NO" +openssl req -x509 -in req-san.pem -out $outname -key rsa-pri-1024.pem \ + -config san.cnf -extensions subj_alt_name +/bin/cp san.cnf $outname.san + +#--- Non-ASCII Subject --------------------------------------------------------------------- +echo -e "\n generating self signed root cert. with Subject containing UTF-8 characters ..." +outname=cert-ss-san-utf8.pem +#subject="/O=HĕĂƲÿ ʍếʈặḻ Récördŝ/OU=㈧A㉁ォBC/CN=Johnny Guitar/C=NO" +subject=$'/O=H\xc4\x95\xc4\x82\xc6\xb2\xc3\xbf \xca\x8d\xe1\xba\xbf\xca\x88\xe1\xba\xb7\xe1\xb8\xbb R\xc3\xa9c\xc3\xb6rd\xc5\x9d/OU=\xe3\x88\xa7A\xe3\x89\x81\xef\xbd\xabBC/CN=Johnny Guitar/C=NO' +openssl req -out req-san.pem -new -key rsa-pri-1024.pem -utf8 -subj "$subject" +openssl req -x509 -in req-san.pem -out $outname -key rsa-pri-1024.pem \ + -config san.cnf -extensions subj_alt_name -nameopt multiline,utf8,-esc_msb +/bin/cp san.cnf $outname.san + +echo -e "\n cleaning up ..." +/bin/rm rsa-pri-1024.pem rsa-pub-1024.* req*.pem diff --git a/tests/auto/network/ssl/qsslcertificate/certificates/san.cnf b/tests/auto/network/ssl/qsslcertificate/certificates/san.cnf new file mode 100644 index 0000000000..f46a637da4 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/certificates/san.cnf @@ -0,0 +1,5 @@ +[subj_alt_name] +subjectAltName=\ + email:arne@foobar.org,\ + DNS:www.foobar.org,\ + email:bjarne@foobar.org diff --git a/tests/auto/network/ssl/qsslcertificate/more-certificates/badguy-nul-cn.crt b/tests/auto/network/ssl/qsslcertificate/more-certificates/badguy-nul-cn.crt new file mode 100644 index 0000000000..b899733bbd --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/more-certificates/badguy-nul-cn.crt @@ -0,0 +1,81 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1 (0x1) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=GB, ST=Berkshire, L=Newbury, O=My Company Ltd, OU=CA, CN=NULL-friendly CA + Validity + Not Before: Aug 4 07:33:43 2009 GMT + Not After : Aug 2 07:33:43 2019 GMT + Subject: CN=www.bank.com\x00.badguy.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (2048 bit) + Modulus (2048 bit): + 00:cd:26:70:96:a9:a6:5d:3e:9c:ed:0f:08:15:5a: + 7c:17:25:68:68:af:13:b9:ad:41:fa:12:54:e2:84: + 72:7d:58:d1:e2:40:42:c1:59:ed:05:3d:aa:10:53: + 70:00:88:3a:77:a0:c0:56:9e:ac:7d:21:2a:71:44: + 51:08:bc:17:07:da:a8:a3:76:dc:51:bc:1b:8a:f6: + 02:1a:55:bf:46:b4:44:6b:27:5e:be:e5:17:8b:56: + b2:c6:82:36:11:83:a8:bf:f7:2f:0d:17:f6:cd:47: + b5:6f:2b:a6:41:b6:8d:33:5f:ea:ea:8b:b1:1a:e2: + 99:38:ff:59:5b:0a:a1:71:13:ca:37:3f:b9:b0:1e: + 91:9a:c8:93:35:0c:4a:e0:9d:f4:d2:61:c7:4e:5b: + 41:0a:7c:31:54:99:db:f5:65:ce:80:d3:c2:02:37: + 64:fd:54:12:7b:ea:ac:85:59:5c:17:e1:2e:f6:d0: + a8:f2:d0:2e:94:59:2f:c2:a6:5f:da:07:de:7b:2e: + 14:07:ed:e4:27:24:37:9d:09:2e:b1:f9:5a:48:b9: + 80:24:43:e6:cb:c7:6e:35:df:d5:69:34:ff:e6:d6: + 9e:e8:76:66:6e:5f:59:01:3c:96:3b:ec:72:0b:3c: + 1e:95:0f:ce:68:13:9c:22:dd:1b:b5:44:28:50:4a: + 05:7f + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Netscape Comment: + OpenSSL Generated Certificate + X509v3 Subject Key Identifier: + 33:15:24:BE:DA:66:3A:06:8B:D9:27:34:3A:AF:62:40:E4:95:66:5D + X509v3 Authority Key Identifier: + keyid:0A:69:39:5F:9D:30:04:18:08:2E:02:0E:E6:EA:9D:B2:26:F6:E2:6A + + Signature Algorithm: sha1WithRSAEncryption + 32:65:23:1f:c8:d9:53:84:82:d0:0a:eb:14:51:24:03:bc:6c: + 1b:2a:5a:fe:1b:f0:e8:69:0c:2b:19:86:cf:7f:32:76:d8:2b: + d2:cf:8b:c4:d1:b6:5b:9c:60:a3:99:2e:92:72:06:ce:de:8b: + d2:a2:d2:89:7c:13:a9:0b:4e:be:12:09:e5:d6:28:3a:ac:a7: + 26:56:94:7f:13:ee:64:7d:de:94:60:75:c1:bc:55:97:d4:aa: + 13:8e:02:d8:b0:b0:70:53:ae:18:53:ce:aa:b2:2c:85:3e:e3: + f3:e1:26:f3:fa:5c:ee:f8:7b:0b:c6:39:b5:04:33:5e:ae:b8: + 5e:0e:66:cc:a8:c0:6a:0d:ec:60:c1:c5:d9:39:ea:bd:1b:8f: + 1c:7d:16:38:b1:e8:c8:37:01:aa:4b:99:df:e4:0f:10:be:61: + ee:9a:cf:cd:27:05:46:00:60:d8:6a:74:08:32:3c:8b:90:01: + 6a:07:33:0c:6c:90:db:ea:fb:6a:17:1a:76:bb:73:14:27:e1: + a4:7e:d5:dd:30:b1:5d:f2:0e:aa:d4:b2:d5:4c:f6:4f:91:2a: + 07:f4:37:c1:cf:48:19:c5:fe:7e:92:96:a8:df:50:6a:31:92: + a3:b1:14:fe:41:cc:49:62:98:4d:ea:c5:ba:05:2d:49:c3:22: + 72:ef:41:09 +-----BEGIN CERTIFICATE----- +MIIDjTCCAnWgAwIBAgIBATANBgkqhkiG9w0BAQUFADB0MQswCQYDVQQGEwJHQjES +MBAGA1UECBMJQmVya3NoaXJlMRAwDgYDVQQHEwdOZXdidXJ5MRcwFQYDVQQKEw5N +eSBDb21wYW55IEx0ZDELMAkGA1UECxMCQ0ExGTAXBgNVBAMTEE5VTEwtZnJpZW5k +bHkgQ0EwHhcNMDkwODA0MDczMzQzWhcNMTkwODAyMDczMzQzWjAjMSEwHwYDVQQD +Exh3d3cuYmFuay5jb20ALmJhZGd1eS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDNJnCWqaZdPpztDwgVWnwXJWhorxO5rUH6ElTihHJ9WNHiQELB +We0FPaoQU3AAiDp3oMBWnqx9ISpxRFEIvBcH2qijdtxRvBuK9gIaVb9GtERrJ16+ +5ReLVrLGgjYRg6i/9y8NF/bNR7VvK6ZBto0zX+rqi7Ea4pk4/1lbCqFxE8o3P7mw +HpGayJM1DErgnfTSYcdOW0EKfDFUmdv1Zc6A08ICN2T9VBJ76qyFWVwX4S720Kjy +0C6UWS/Cpl/aB957LhQH7eQnJDedCS6x+VpIuYAkQ+bLx24139VpNP/m1p7odmZu +X1kBPJY77HILPB6VD85oE5wi3Ru1RChQSgV/AgMBAAGjezB5MAkGA1UdEwQCMAAw +LAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0G +A1UdDgQWBBQzFSS+2mY6BovZJzQ6r2JA5JVmXTAfBgNVHSMEGDAWgBQKaTlfnTAE +GAguAg7m6p2yJvbiajANBgkqhkiG9w0BAQUFAAOCAQEAMmUjH8jZU4SC0ArrFFEk +A7xsGypa/hvw6GkMKxmGz38ydtgr0s+LxNG2W5xgo5kuknIGzt6L0qLSiXwTqQtO +vhIJ5dYoOqynJlaUfxPuZH3elGB1wbxVl9SqE44C2LCwcFOuGFPOqrIshT7j8+Em +8/pc7vh7C8Y5tQQzXq64Xg5mzKjAag3sYMHF2TnqvRuPHH0WOLHoyDcBqkuZ3+QP +EL5h7prPzScFRgBg2Gp0CDI8i5ABagczDGyQ2+r7ahcadrtzFCfhpH7V3TCxXfIO +qtSy1Uz2T5EqB/Q3wc9IGcX+fpKWqN9QajGSo7EU/kHMSWKYTerFugUtScMicu9B +CQ== +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslcertificate/more-certificates/badguy-nul-san.crt b/tests/auto/network/ssl/qsslcertificate/more-certificates/badguy-nul-san.crt new file mode 100644 index 0000000000..d897c3915e --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/more-certificates/badguy-nul-san.crt @@ -0,0 +1,83 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 0 (0x0) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=GB, ST=Berkshire, L=Newbury, O=My Company Ltd, OU=CA, CN=NULL-friendly CA + Validity + Not Before: Aug 4 06:53:05 2009 GMT + Not After : Aug 2 06:53:05 2019 GMT + Subject: CN=www.badguy.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (2048 bit) + Modulus (2048 bit): + 00:cd:26:70:96:a9:a6:5d:3e:9c:ed:0f:08:15:5a: + 7c:17:25:68:68:af:13:b9:ad:41:fa:12:54:e2:84: + 72:7d:58:d1:e2:40:42:c1:59:ed:05:3d:aa:10:53: + 70:00:88:3a:77:a0:c0:56:9e:ac:7d:21:2a:71:44: + 51:08:bc:17:07:da:a8:a3:76:dc:51:bc:1b:8a:f6: + 02:1a:55:bf:46:b4:44:6b:27:5e:be:e5:17:8b:56: + b2:c6:82:36:11:83:a8:bf:f7:2f:0d:17:f6:cd:47: + b5:6f:2b:a6:41:b6:8d:33:5f:ea:ea:8b:b1:1a:e2: + 99:38:ff:59:5b:0a:a1:71:13:ca:37:3f:b9:b0:1e: + 91:9a:c8:93:35:0c:4a:e0:9d:f4:d2:61:c7:4e:5b: + 41:0a:7c:31:54:99:db:f5:65:ce:80:d3:c2:02:37: + 64:fd:54:12:7b:ea:ac:85:59:5c:17:e1:2e:f6:d0: + a8:f2:d0:2e:94:59:2f:c2:a6:5f:da:07:de:7b:2e: + 14:07:ed:e4:27:24:37:9d:09:2e:b1:f9:5a:48:b9: + 80:24:43:e6:cb:c7:6e:35:df:d5:69:34:ff:e6:d6: + 9e:e8:76:66:6e:5f:59:01:3c:96:3b:ec:72:0b:3c: + 1e:95:0f:ce:68:13:9c:22:dd:1b:b5:44:28:50:4a: + 05:7f + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Netscape Comment: + OpenSSL Generated Certificate + X509v3 Subject Key Identifier: + 33:15:24:BE:DA:66:3A:06:8B:D9:27:34:3A:AF:62:40:E4:95:66:5D + X509v3 Authority Key Identifier: + keyid:0A:69:39:5F:9D:30:04:18:08:2E:02:0E:E6:EA:9D:B2:26:F6:E2:6A + + X509v3 Subject Alternative Name: + DNS:www.bank.com + Signature Algorithm: sha1WithRSAEncryption + 27:6e:7d:b3:a9:86:52:57:6a:a0:c6:30:6c:1e:94:09:a7:6f: + ad:fe:11:9f:be:32:8d:01:7b:8b:94:66:d7:7c:b6:b1:90:fc: + e4:f5:b6:32:bc:6c:71:23:b1:18:88:d6:47:bc:da:07:c7:5e: + 46:71:3a:e6:40:6e:c1:7f:1d:56:96:70:65:d8:51:a9:dc:9e: + a5:06:00:98:e7:1e:10:bc:82:ba:00:e5:4e:a2:0f:3e:ec:8a: + dd:6f:c6:c9:c1:ec:ed:6d:7c:31:3e:66:87:47:a1:8b:15:3c: + 21:7e:ec:21:78:3d:21:70:72:ba:70:c3:64:f8:1d:4f:d9:d0: + 27:3c:3e:7e:a2:59:ae:be:9a:d3:00:44:a7:72:3a:e3:3f:c8: + 9b:c5:8f:b1:94:fe:00:0f:6e:b8:14:88:f1:03:50:91:51:af: + f0:1e:f7:b8:5a:a4:57:35:2d:f1:ad:c8:ae:dd:29:61:14:7d: + ea:d1:34:80:5c:1b:fd:eb:43:dc:21:6d:c6:44:f9:3b:54:76: + c4:91:5b:ac:a4:8e:72:e7:d8:24:ff:a7:5a:c0:ef:27:c3:d7: + e4:f9:7f:55:8d:0d:30:ec:a2:d9:6d:c8:76:f4:be:94:3d:12: + 32:4a:91:4f:db:c3:e7:76:07:5a:12:97:18:b7:15:00:98:59: + 21:89:3e:35 +-----BEGIN CERTIFICATE----- +MIIDrTCCApWgAwIBAgIBADANBgkqhkiG9w0BAQUFADB0MQswCQYDVQQGEwJHQjES +MBAGA1UECBMJQmVya3NoaXJlMRAwDgYDVQQHEwdOZXdidXJ5MRcwFQYDVQQKEw5N +eSBDb21wYW55IEx0ZDELMAkGA1UECxMCQ0ExGTAXBgNVBAMTEE5VTEwtZnJpZW5k +bHkgQ0EwHhcNMDkwODA0MDY1MzA1WhcNMTkwODAyMDY1MzA1WjAZMRcwFQYDVQQD +Ew53d3cuYmFkZ3V5LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AM0mcJappl0+nO0PCBVafBclaGivE7mtQfoSVOKEcn1Y0eJAQsFZ7QU9qhBTcACI +OnegwFaerH0hKnFEUQi8FwfaqKN23FG8G4r2AhpVv0a0RGsnXr7lF4tWssaCNhGD +qL/3Lw0X9s1HtW8rpkG2jTNf6uqLsRrimTj/WVsKoXETyjc/ubAekZrIkzUMSuCd +9NJhx05bQQp8MVSZ2/VlzoDTwgI3ZP1UEnvqrIVZXBfhLvbQqPLQLpRZL8KmX9oH +3nsuFAft5CckN50JLrH5Wki5gCRD5svHbjXf1Wk0/+bWnuh2Zm5fWQE8ljvscgs8 +HpUPzmgTnCLdG7VEKFBKBX8CAwEAAaOBpDCBoTAJBgNVHRMEAjAAMCwGCWCGSAGG ++EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQU +MxUkvtpmOgaL2Sc0Oq9iQOSVZl0wHwYDVR0jBBgwFoAUCmk5X50wBBgILgIO5uqd +sib24mowJgYDVR0RBB8wHYIbd3d3LmJhbmsuY29tAHd3dy5iYWRndXkuY29tMA0G +CSqGSIb3DQEBBQUAA4IBAQAnbn2zqYZSV2qgxjBsHpQJp2+t/hGfvjKNAXuLlGbX +fLaxkPzk9bYyvGxxI7EYiNZHvNoHx15GcTrmQG7Bfx1WlnBl2FGp3J6lBgCY5x4Q +vIK6AOVOog8+7Irdb8bJweztbXwxPmaHR6GLFTwhfuwheD0hcHK6cMNk+B1P2dAn +PD5+olmuvprTAESncjrjP8ibxY+xlP4AD264FIjxA1CRUa/wHve4WqRXNS3xrciu +3SlhFH3q0TSAXBv960PcIW3GRPk7VHbEkVuspI5y59gk/6dawO8nw9fk+X9VjQ0w +7KLZbch29L6UPRIySpFP28PndgdaEpcYtxUAmFkhiT41 +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted-google.com-diginotar.pem b/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted-google.com-diginotar.pem new file mode 100644 index 0000000000..12bbcae082 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted-google.com-diginotar.pem @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFKDCCBBCgAwIBAgIQBeLmpM0J6lTWZbB1/iKiVjANBgkqhkiG9w0BAQUFADBm +MQswCQYDVQQGEwJOTDESMBAGA1UEChMJRGlnaU5vdGFyMSEwHwYDVQQDExhEaWdp +Tm90YXIgUHVibGljIENBIDIwMjUxIDAeBgkqhkiG9w0BCQEWEWluZm9AZGlnaW5v +dGFyLm5sMB4XDTExMDcxMDE5MDYzMFoXDTEzMDcwOTE5MDYzMFowajELMAkGA1UE +BhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxFjAUBgNVBAcTDU1vdW50YWluIFZp +ZXcxFzAVBgNVBAUTDlBLMDAwMjI5MjAwMDAyMRUwEwYDVQQDEwwqLmdvb2dsZS5j +b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDNbeKubCV0aCxhOiOS +CSQ/w9HXTYuD5BLKuiqXNw3setdTymeJz2L8aWOHo3nicFNDVwWTgwWomGNr2J6Q +7g1iINNSW0rR4E1l2szRkcnAY6c6i/Eke93nF4i2hDsnIBveolF5yjpuRm73uQQD +ulHjA3BFRF/PTi0fw2/Yt+8ieoMuNcMWN6Eou5Gqt5YZkWv176ofeCbsBmMrP87x +OhhtTDckCapk4VQZG2XrfzZcV6tdzCp5TI8uHdu17cdzXm1imZ8tyvzFeiCEOQN8 +vPNzB/fIr3CJQ5q4uM5aKT3DD5PeVzf4rfJKQNgCTWiIBc9XcWEUuszwAsnmg7e2 +EJRdAgMBAAGjggHMMIIByDA6BggrBgEFBQcBAQQuMCwwKgYIKwYBBQUHMAGGHmh0 +dHA6Ly92YWxpZGF0aW9uLmRpZ2lub3Rhci5ubDAfBgNVHSMEGDAWgBTfM8Cvkv43 +/LbYFhbQ2bGR1fpupTAJBgNVHRMEAjAAMIHGBgNVHSAEgb4wgbswgbgGDmCEEAGH +aQEBAQIEAQICMIGlMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2lub3Rhci5u +bC9jcHMwegYIKwYBBQUHAgIwbhpsQ29uZGl0aW9ucywgYXMgbWVudGlvbmVkIG9u +IG91ciB3ZWJzaXRlICh3d3cuZGlnaW5vdGFyLm5sKSwgYXJlIGFwcGxpY2FibGUg +dG8gYWxsIG91ciBwcm9kdWN0cyBhbmQgc2VydmljZXMuMEkGA1UdHwRCMEAwPqA8 +oDqGOGh0dHA6Ly9zZXJ2aWNlLmRpZ2lub3Rhci5ubC9jcmwvcHVibGljMjAyNS9s +YXRlc3RDUkwuY3JsMA4GA1UdDwEB/wQEAwIEsDAbBgNVHREEFDASgRBhZG1pbkBn +b29nbGUuY29tMB0GA1UdDgQWBBQHSn0WJzIo0eMBMQUNsMqN6eF/7TANBgkqhkiG +9w0BAQUFAAOCAQEAAs5dL7N9wzRJkI4Aq4lC5t8j5ZadqnqUcgYLADzSv4ExytNH +UY2nH6iVTihC0UPSsILWraoeApdT7Rphz/8DLQEBRGdeKWAptNM3EbiXtQaZT2uB +pidL8UoafX0kch3f71Y1scpBEjvu5ZZLnjg0A8AL0tnsereOVdDpU98bKqdbbrnM +FRmBlSf7xdaNca6JJHeEpga4E9Ty683CmccrSGXdU2tTCuHEJww+iOAUtPIZcsum +U7/eYeY1pMyGLyIjbNgRY7nDzRwvM/BsbL9eh4/mSQj/4nncqJd22sVQpCggQiVK +baB2sVGcVNBkK55bT8gPqnx8JypubyUvayzZGg== +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted1.pem b/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted1.pem new file mode 100644 index 0000000000..7fc0a054c0 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted1.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDDzCCAnigAwIBAgIQBH7L6fylX3vQnq424QyuHjANBgkqhkiG9w0BAQUFADBf +MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50 +ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRgwFgYDVQQDEw9tYWlsLmdvb2dsZS5jb20w +HhcNMTEwODMwMTAxOTI2WhcNMjEwODI3MTAxOTI2WjBfMQswCQYDVQQGEwJBVTET +MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ +dHkgTHRkMRgwFgYDVQQDEw9tYWlsLmdvb2dsZS5jb20wgZ8wDQYJKoZIhvcNAQEB +BQADgY0AMIGJAoGBANOvJQGm9vyX8f61SP1XDp23sEDS2SAsDNIxdeONmFthfQRh +EBlJMNDByegnImZPMN4tA2T2iKcvdkxyQhC9vnQ+HEqJxxu0EhOwO+UdsSII7Lns +yQZVj2QAoTvC0+MFHPo+wl39JEe3ZytNwQZLjfZSLdS/j0cAyoTkFNconK0bAgMB +AAGjgcswgcgwHQYDVR0OBBYEFGFxxhfk1fvT8zPfLKPE0YYp9HZEMIGYBgNVHSME +gZAwgY2AFGFxxhfk1fvT8zPfLKPE0YYp9HZEoWOkYTBfMQswCQYDVQQGEwJBVTET +MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ +dHkgTHRkMRgwFgYDVQQDEw9tYWlsLmdvb2dsZS5jb22CEAR+y+n8pV970J6uNuEM +rh4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAldVSTJ4lNG9Qxx2GG +/y/ccH/BBYkpPXBklKP/U528Zk48jypWsbnZ07rQrPIlSXYQHffdNH3JsEiF/el9 +bIu+6tGJzmjqvtl5fD5S+yBvI3ySx3Fz6lWmUlqT6UY1X8Oob2PsR6u6mfT8Q1da +d02x97EUSiAzYvzxndqD6g8R1w== +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted2.pem b/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted2.pem new file mode 100644 index 0000000000..3c8489fbcd --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted2.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDDjCCAnegAwIBAgIRAPXIavNhYvE6ZPVPbclYfAYwDQYJKoZIhvcNAQEFBQAw +XjELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGElu +dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEXMBUGA1UEAxMOd3d3Lmdvb2dsZS5jb20w +HhcNMTEwODMwMTAxOTUxWhcNMjEwODI3MTAxOTUxWjBeMQswCQYDVQQGEwJBVTET +MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ +dHkgTHRkMRcwFQYDVQQDEw53d3cuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEF +AAOBjQAwgYkCgYEAriNo6jkVPi+gyynL2YiPBqDsBa4MuM4rQwM7vcHDRO9GizKi +6gV7/loaqvr7zqKnHmoARP6OqxDMMEWfZ4QGJjToVKS6srE7dPJEh1lRzK+bsl02 +xGCP/RKJqnZcW1oUpFUceRBQ8TWynZ1L7cE/YUlOhqXnMO9aPibqbWj8AGcCAwEA +AaOByzCByDAdBgNVHQ4EFgQUo/E6UVU6oRgxQF8yftx0/9dvzMgwgZgGA1UdIwSB +kDCBjYAUo/E6UVU6oRgxQF8yftx0/9dvzMihYqRgMF4xCzAJBgNVBAYTAkFVMRMw +EQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0 +eSBMdGQxFzAVBgNVBAMTDnd3dy5nb29nbGUuY29tghEA9chq82Fi8Tpk9U9tyVh8 +BjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAESHOPB9eAsTKkO4lY8d +EKr2XrRqUkk5KqMFraAHWRekJFwl0R39Q6MDjRHr6NpPJkSKuBPppkr9nk6WDfpt +TG+oN3Yb1rFmJv6eZw8ud9btoquc8jAtaQnihbjFPEP9cCLBw3Gz1C/JBjDw1tgK +zXKNU+jMZYk7c5Z64DocggBH +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted3.pem b/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted3.pem new file mode 100644 index 0000000000..fa458cca36 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted3.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDETCCAnqgAwIBAgIRANdVj9r18RBbshMoK3B3KaMwDQYJKoZIhvcNAQEFBQAw +XzELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGElu +dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEYMBYGA1UEAxMPbG9naW4ueWFob28uY29t +MB4XDTExMDgzMDEwMjAzNloXDTIxMDgyNzEwMjAzNlowXzELMAkGA1UEBhMCQVUx +EzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMg +UHR5IEx0ZDEYMBYGA1UEAxMPbG9naW4ueWFob28uY29tMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQC3TVwiPfNClgadKYuX50szBlIaVWDd+UFHsakbGk0bgqlu +YKgC/cmbjgOyrmsEC2HbJISceTz9CK6mCewEgGFWuq6gjYi4UnSFk1o6h6WJV86A +tLpeVti7lN9/04IW7XHyxuhvS71V+JWlqfu3zboyaVoMIlHDrPFtRiAh7vTTjQID +AQABo4HMMIHJMB0GA1UdDgQWBBQrVExlI4rixsrub6IMDDsLPNB4ezCBmQYDVR0j +BIGRMIGOgBQrVExlI4rixsrub6IMDDsLPNB4e6FjpGEwXzELMAkGA1UEBhMCQVUx +EzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMg +UHR5IEx0ZDEYMBYGA1UEAxMPbG9naW4ueWFob28uY29tghEA11WP2vXxEFuyEygr +cHcpozAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAFB5SF2ujZzH9KMj +m4ZDTsyy94/YQZdwgLncKUYTxmZe4BdX+42j799pCG+UeQGwqz9hU/soPgibAvGT +1KCEGQ6qtWSqKJYmC8VKenYvvEFIySj5cbSFmId5aNwhjl8AYoLuM0E+2FRVJQ6a +pWyzYUIOqhsCkt9hEPsDVP4hIhP+ +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted4.pem b/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted4.pem new file mode 100644 index 0000000000..53761bb36a --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted4.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDDzCCAnigAwIBAgIQOSpDTw4H3x+KowXeNODCKTANBgkqhkiG9w0BAQUFADBf +MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50 +ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRgwFgYDVQQDEw9sb2dpbi55YWhvby5jb20w +HhcNMTEwODMwMTAyMTE5WhcNMjEwODI3MTAyMTE5WjBfMQswCQYDVQQGEwJBVTET +MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ +dHkgTHRkMRgwFgYDVQQDEw9sb2dpbi55YWhvby5jb20wgZ8wDQYJKoZIhvcNAQEB +BQADgY0AMIGJAoGBAML+Z5hpY4VfSeTPbMCGaqe5shwcw3yW/egYY6cXcfxtUkjs +Eai48hP/sqtQeFwi3puJ7HO2iGUX72/UnO0t9qwEGtGOHS1qqAYdTcncY5pTpO9L +e4Tn6CkPwFE4VNXVU96xPlUjP/KBZ43VH6gW3M1xDI0DmNh2QUXHN5ErQE8nAgMB +AAGjgcswgcgwHQYDVR0OBBYEFI+gG6PT7vQtUVn2xf+wCYZV5Ht8MIGYBgNVHSME +gZAwgY2AFI+gG6PT7vQtUVn2xf+wCYZV5Ht8oWOkYTBfMQswCQYDVQQGEwJBVTET +MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ +dHkgTHRkMRgwFgYDVQQDEw9sb2dpbi55YWhvby5jb22CEDkqQ08OB98fiqMF3jTg +wikwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQA3eZG/dOXrL7YBPCA8 +joDGfhra98T1iCpul4/L/L4dt/9+QVAu+agbZmHWLYzuAvuB1zj8go0BLIE7b4ap +HPLFYXV3iAWjIRhNEix4FWohlds1B+IwpvWdsl3Op1pZfHQ0yq8wFGawdQTAKUII +lLu1cd6E8B6pCfWwSr+9h6gnTg== +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted5.pem b/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted5.pem new file mode 100644 index 0000000000..81211ee694 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted5.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDDzCCAnigAwIBAgIQPnXO1GtpMCEhiDCuhqgqcTANBgkqhkiG9w0BAQUFADBf +MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50 +ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRgwFgYDVQQDEw9sb2dpbi55YWhvby5jb20w +HhcNMTEwODMwMTAyMTQ4WhcNMjEwODI3MTAyMTQ4WjBfMQswCQYDVQQGEwJBVTET +MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ +dHkgTHRkMRgwFgYDVQQDEw9sb2dpbi55YWhvby5jb20wgZ8wDQYJKoZIhvcNAQEB +BQADgY0AMIGJAoGBAL5IeUbbQ7HxCBLQaOASV2HyI1tRwPm/7JNsRfh5ipM1sCWE +xnPoqFznX6ZUKi8d61/EIycwUd+FvOp9zoRxDlngoRdhqMCTTG3JfxNf6ZXJPCYd +qPjOKAkMwyG8bbhGCsoCws9b2rpN9536qVXc2QR39F9/ZE5t73oKtEd1fyNnAgMB +AAGjgcswgcgwHQYDVR0OBBYEFNXCkfGdW3WYzBBqB2jWppl6sL99MIGYBgNVHSME +gZAwgY2AFNXCkfGdW3WYzBBqB2jWppl6sL99oWOkYTBfMQswCQYDVQQGEwJBVTET +MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ +dHkgTHRkMRgwFgYDVQQDEw9sb2dpbi55YWhvby5jb22CED51ztRraTAhIYgwroao +KnEwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQCOeih7N7fmkqECWZD/ +bYsFLtbKOwD4YMPIV2wayvaLg2dFwqhBkGml+5ulOh5KTiUh0/nnGmGAU/3K9nt5 +TCMjwMGHm/C5pp7THQriiY8Qw0QVtnFiJGnjblhAbJVIvBJJ42/Qq7T4IzEwqShW +hO2g1M0MUGiOw4vyXJRGc4dg2w== +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted6.pem b/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted6.pem new file mode 100644 index 0000000000..4264a678f1 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted6.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDETCCAnqgAwIBAgIRAOkCi5V45BXcGnEKK4gVREcwDQYJKoZIhvcNAQEFBQAw +XzELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGElu +dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEYMBYGA1UEAxMPbG9naW4uc2t5cGUuY29t +MB4XDTExMDgzMDEwMjIxOFoXDTIxMDgyNzEwMjIxOFowXzELMAkGA1UEBhMCQVUx +EzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMg +UHR5IEx0ZDEYMBYGA1UEAxMPbG9naW4uc2t5cGUuY29tMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQDAAkuUFRdz1bm95jFsyQRb4PBrly4pJK4f5pSZy99eY8FM +iZbr7brnkiheN4qeJfS1dv/B/B4kAVoGD2Y0KmpZ5ZZYmm3TxopoP0Yeg6juWNKa +iFlfKQIvk0GEmD8oGfEqW1+72p040jAuTn+OQx+7VpydqB/RJoY8qK3zEXbB7wID +AQABo4HMMIHJMB0GA1UdDgQWBBSE9drle8VwSlwYIX5mKaIEvQYX/zCBmQYDVR0j +BIGRMIGOgBSE9drle8VwSlwYIX5mKaIEvQYX/6FjpGEwXzELMAkGA1UEBhMCQVUx +EzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMg +UHR5IEx0ZDEYMBYGA1UEAxMPbG9naW4uc2t5cGUuY29tghEA6QKLlXjkFdwacQor +iBVERzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBACm4/Q6e001Fg9uu +ieqHOAxfBafkOsXtgKqTFFdpt9Hbmo9j/BfvrtvfFN5Ph4fV46useGjW79P1IeNK ++KqcaqOKy2q6j1em4j8C5Mx6S4ksZJC25jIPEM7cdxiDGcV505X5mynlu1+WkZAf +nQhnJpA8o3p462ON4S+GXTfOLfoN +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted7.pem b/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted7.pem new file mode 100644 index 0000000000..03ab7d46b0 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted7.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDGjCCAoOgAwIBAgIRAJI51TSPQNFpWnRUcOHyP0MwDQYJKoZIhvcNAQEFBQAw +YjELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGElu +dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEbMBkGA1UEAxMSYWRkb25zLm1vemlsbGEu +b3JnMB4XDTExMDgzMDEwMjIzOVoXDTIxMDgyNzEwMjIzOVowYjELMAkGA1UEBhMC +QVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdp +dHMgUHR5IEx0ZDEbMBkGA1UEAxMSYWRkb25zLm1vemlsbGEub3JnMIGfMA0GCSqG +SIb3DQEBAQUAA4GNADCBiQKBgQC9Yo3DJvs+vewelK5qoK+0Xn4zeRIQ/wJCrQBg +5tNXja+NZsHb0/enlwAZ3dAwCfI0G12mTowgSuZyDX7oN2+G+k6Q5LPnGhj1E5s5 +OJ+ZYsAjTdU0SCAPquva55+jyhqo5w/B5Il7w84mppUrAZgqEGhYkhXeDOULe1Vm +4iLMQwIDAQABo4HPMIHMMB0GA1UdDgQWBBSb8lAIgOnUitVOuZIx3s+5MN1sBzCB +nAYDVR0jBIGUMIGRgBSb8lAIgOnUitVOuZIx3s+5MN1sB6FmpGQwYjELMAkGA1UE +BhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdp +ZGdpdHMgUHR5IEx0ZDEbMBkGA1UEAxMSYWRkb25zLm1vemlsbGEub3JnghEAkjnV +NI9A0WladFRw4fI/QzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAFpg +tcp6ynOZ/hwr2axTYK+4rtXmTUJdsg7NUHhXSwGANAuYuc7PYwqtmz3B2W90t3TA +D3pNMRBEXrBXufC0p9vu/hQgb+mdQ7DG6j1Gkkpq4Sq/Puv1bO96KpAufmevnWWB +48kzCgAdfk/N04k7kdPHCp5MjjgmY3kGdsg+jroj +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted8.pem b/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted8.pem new file mode 100644 index 0000000000..acef06ffb0 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted8.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDDjCCAnegAwIBAgIRALC3Ez7Qlvm1b66RyHS9OsAwDQYJKoZIhvcNAQEFBQAw +XjELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGElu +dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEXMBUGA1UEAxMObG9naW4ubGl2ZS5jb20w +HhcNMTEwODMwMTAyMzA0WhcNMjEwODI3MTAyMzA0WjBeMQswCQYDVQQGEwJBVTET +MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ +dHkgTHRkMRcwFQYDVQQDEw5sb2dpbi5saXZlLmNvbTCBnzANBgkqhkiG9w0BAQEF +AAOBjQAwgYkCgYEA5JK77+1zFatj8xeItZaDW5XaU+ssc5jq2Ww4ANaxJi+wdRVU +qVLauEJPKEXC51fuYHa7U8yoSTgJZA7JL07cdSxgsVj8RR4Uf5k4Jf5Vdz5w8+TT +W33I3zurA2xB+wfup2VPmS8Alg07w6POrzlQYgtcUcELZhjcdypk96NOdPECAwEA +AaOByzCByDAdBgNVHQ4EFgQUTWM20msyZUcNEYOyNbgevBPIoF8wgZgGA1UdIwSB +kDCBjYAUTWM20msyZUcNEYOyNbgevBPIoF+hYqRgMF4xCzAJBgNVBAYTAkFVMRMw +EQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0 +eSBMdGQxFzAVBgNVBAMTDmxvZ2luLmxpdmUuY29tghEAsLcTPtCW+bVvrpHIdL06 +wDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBACQqnu1KywflnNiqIU01 +95z/qjmZTvSWafM/HLOFUg8ls7g32l5gCCrsc8ypiCff+S3+9teYQOII7oW3hzw4 +BH10m0LTRxGig5U9XVkH4076SALkPoky1z+onMmLk7AE96kSJ+rq30VvcSSl9BaW +DwcQGIhCZANgZrNW9AJ+cweQ +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted9.pem b/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted9.pem new file mode 100644 index 0000000000..cc99b08e5f --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted9.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDDjCCAnegAwIBAgIRANjzX063hystqwaS4xU4L7AwDQYJKoZIhvcNAQEFBQAw +XjELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGElu +dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEXMBUGA1UEAxMOZ2xvYmFsIHRydXN0ZWUw +HhcNMTEwODMwMTAyMzI1WhcNMjEwODI3MTAyMzI1WjBeMQswCQYDVQQGEwJBVTET +MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ +dHkgTHRkMRcwFQYDVQQDEw5nbG9iYWwgdHJ1c3RlZTCBnzANBgkqhkiG9w0BAQEF +AAOBjQAwgYkCgYEAw0CpZXrd6oI8/PHo8/bnNwB7UA7OKsRFuqKljr9F5lCJAVkT +zRDlXuTDeXGfQiYhJTF7GRunPRJ7O5SsflwoxktH/F91yFpgxY5DqRZ1ZHdZgW3b +W0uweBxJv9684ihXY4YR3Yznf0js7YAgVd8a3qhOUODiC/Beb8tmDBNL2c0CAwEA +AaOByzCByDAdBgNVHQ4EFgQU/rUvQ2rG7pg2muUiLm6V93ZgmGcwgZgGA1UdIwSB +kDCBjYAU/rUvQ2rG7pg2muUiLm6V93ZgmGehYqRgMF4xCzAJBgNVBAYTAkFVMRMw +EQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0 +eSBMdGQxFzAVBgNVBAMTDmdsb2JhbCB0cnVzdGVlghEA2PNfTreHKy2rBpLjFTgv +sDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBALvmmUx47DcUAIsPkI0Y +DTvcXV6k2JHwdrdlPsrn9A5TLppfxPNrDMiweitr8cMkKEtkm6LRTt2yvDFcMIwy +/+F5+XO0k9sKde6xHfWvOvndIycj3Lt4tIRW8jUauJENyMa4M57qpqkgkJEmflkI +/pb9fQZbfg70o9wvbNK0RONo +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslcertificate/more-certificates/cert-large-expiration-date.pem b/tests/auto/network/ssl/qsslcertificate/more-certificates/cert-large-expiration-date.pem new file mode 100644 index 0000000000..416dd4da04 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/more-certificates/cert-large-expiration-date.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICWjCCAcOgAwIBAgIJAM7bMShFxAVAMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQwIBcNMTAwODA0MDk1MzQxWhgPMjA1MTA4MjkwOTUzNDFa +MEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJ +bnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJ +AoGBAM2q22/WNMmn8cC+5EEYGeICySLmp9W6Ay6eKHr0Xxp3X3epETuPfvAuxp7r +OtkS18EMUegkUj8jw0IMEcbyHKFC/rTCaYOt93CxGBXMIChiMPAsFeYzGa/D6xzA +kfcRaJRQ+Ek3CDLXPnXfo7xpABXezYcPXAJrgsgBfWrwHdxzAgMBAAGjUDBOMB0G +A1UdDgQWBBSKbhnnl5uP2X+zuwFP6GovUpUN2TAfBgNVHSMEGDAWgBSKbhnnl5uP +2X+zuwFP6GovUpUN2TAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAKF0 +jl02liwFfupmzC5oxz2T3IyjEa21fm7QBMQJvQr5OTuX1/C7DAl7g/6/h7BH6JS3 +qpx5rXGet8SZmG8dODL4o3U4xOXnNzch7HtQixWwlx4XnFAXPMHflFX7YC5QQNHq +I8Y8IW+XjAYWpYJywWMUZIbr1/9y9gn1beYEE3pq +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslcertificate/more-certificates/cert-large-expiration-date.txt.0.9.8 b/tests/auto/network/ssl/qsslcertificate/more-certificates/cert-large-expiration-date.txt.0.9.8 new file mode 100644 index 0000000000..20500b221f --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/more-certificates/cert-large-expiration-date.txt.0.9.8 @@ -0,0 +1,42 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + ce:db:31:28:45:c4:05:40 + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd + Validity + Not Before: Aug 4 09:53:41 2010 GMT + Not After : Aug 29 09:53:41 2051 GMT + Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (1024 bit) + Modulus (1024 bit): + 00:cd:aa:db:6f:d6:34:c9:a7:f1:c0:be:e4:41:18: + 19:e2:02:c9:22:e6:a7:d5:ba:03:2e:9e:28:7a:f4: + 5f:1a:77:5f:77:a9:11:3b:8f:7e:f0:2e:c6:9e:eb: + 3a:d9:12:d7:c1:0c:51:e8:24:52:3f:23:c3:42:0c: + 11:c6:f2:1c:a1:42:fe:b4:c2:69:83:ad:f7:70:b1: + 18:15:cc:20:28:62:30:f0:2c:15:e6:33:19:af:c3: + eb:1c:c0:91:f7:11:68:94:50:f8:49:37:08:32:d7: + 3e:75:df:a3:bc:69:00:15:de:cd:87:0f:5c:02:6b: + 82:c8:01:7d:6a:f0:1d:dc:73 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + 8A:6E:19:E7:97:9B:8F:D9:7F:B3:BB:01:4F:E8:6A:2F:52:95:0D:D9 + X509v3 Authority Key Identifier: + keyid:8A:6E:19:E7:97:9B:8F:D9:7F:B3:BB:01:4F:E8:6A:2F:52:95:0D:D9 + + X509v3 Basic Constraints: + CA:TRUE + Signature Algorithm: sha1WithRSAEncryption + a1:74:8e:5d:36:96:2c:05:7e:ea:66:cc:2e:68:c7:3d:93:dc: + 8c:a3:11:ad:b5:7e:6e:d0:04:c4:09:bd:0a:f9:39:3b:97:d7: + f0:bb:0c:09:7b:83:fe:bf:87:b0:47:e8:94:b7:aa:9c:79:ad: + 71:9e:b7:c4:99:98:6f:1d:38:32:f8:a3:75:38:c4:e5:e7:37: + 37:21:ec:7b:50:8b:15:b0:97:1e:17:9c:50:17:3c:c1:df:94: + 55:fb:60:2e:50:40:d1:ea:23:c6:3c:21:6f:97:8c:06:16:a5: + 82:72:c1:63:14:64:86:eb:d7:ff:72:f6:09:f5:6d:e6:04:13: + 7a:6a diff --git a/tests/auto/network/ssl/qsslcertificate/more-certificates/cert-large-expiration-date.txt.1.0.0 b/tests/auto/network/ssl/qsslcertificate/more-certificates/cert-large-expiration-date.txt.1.0.0 new file mode 100644 index 0000000000..b2ccb2751e --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/more-certificates/cert-large-expiration-date.txt.1.0.0 @@ -0,0 +1,42 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + ce:db:31:28:45:c4:05:40 + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd + Validity + Not Before: Aug 4 09:53:41 2010 GMT + Not After : Aug 29 09:53:41 2051 GMT + Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1024 bit) + Modulus: + 00:cd:aa:db:6f:d6:34:c9:a7:f1:c0:be:e4:41:18: + 19:e2:02:c9:22:e6:a7:d5:ba:03:2e:9e:28:7a:f4: + 5f:1a:77:5f:77:a9:11:3b:8f:7e:f0:2e:c6:9e:eb: + 3a:d9:12:d7:c1:0c:51:e8:24:52:3f:23:c3:42:0c: + 11:c6:f2:1c:a1:42:fe:b4:c2:69:83:ad:f7:70:b1: + 18:15:cc:20:28:62:30:f0:2c:15:e6:33:19:af:c3: + eb:1c:c0:91:f7:11:68:94:50:f8:49:37:08:32:d7: + 3e:75:df:a3:bc:69:00:15:de:cd:87:0f:5c:02:6b: + 82:c8:01:7d:6a:f0:1d:dc:73 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + 8A:6E:19:E7:97:9B:8F:D9:7F:B3:BB:01:4F:E8:6A:2F:52:95:0D:D9 + X509v3 Authority Key Identifier: + keyid:8A:6E:19:E7:97:9B:8F:D9:7F:B3:BB:01:4F:E8:6A:2F:52:95:0D:D9 + + X509v3 Basic Constraints: + CA:TRUE + Signature Algorithm: sha1WithRSAEncryption + a1:74:8e:5d:36:96:2c:05:7e:ea:66:cc:2e:68:c7:3d:93:dc: + 8c:a3:11:ad:b5:7e:6e:d0:04:c4:09:bd:0a:f9:39:3b:97:d7: + f0:bb:0c:09:7b:83:fe:bf:87:b0:47:e8:94:b7:aa:9c:79:ad: + 71:9e:b7:c4:99:98:6f:1d:38:32:f8:a3:75:38:c4:e5:e7:37: + 37:21:ec:7b:50:8b:15:b0:97:1e:17:9c:50:17:3c:c1:df:94: + 55:fb:60:2e:50:40:d1:ea:23:c6:3c:21:6f:97:8c:06:16:a5: + 82:72:c1:63:14:64:86:eb:d7:ff:72:f6:09:f5:6d:e6:04:13: + 7a:6a diff --git a/tests/auto/network/ssl/qsslcertificate/more-certificates/cert-large-serial-number.pem b/tests/auto/network/ssl/qsslcertificate/more-certificates/cert-large-serial-number.pem new file mode 100644 index 0000000000..ecb6c35632 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/more-certificates/cert-large-serial-number.pem @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICGjCCAYMCFAECAwQFBgcICRCqu8zd7v8XGBkgMA0GCSqGSIb3DQEBBQUAMEwx +CzAJBgNVBAYTAkdCMRIwEAYDVQQIEwlCZXJrc2hpcmUxEDAOBgNVBAcTB05ld2J1 +cnkxFzAVBgNVBAoTDk15IENvbXBhbnkgTHRkMB4XDTEwMDYwMTE1MDI0MVoXDTEx +MDYwMTE1MDI0MVowTDELMAkGA1UEBhMCR0IxEjAQBgNVBAgTCUJlcmtzaGlyZTEQ +MA4GA1UEBxMHTmV3YnVyeTEXMBUGA1UEChMOTXkgQ29tcGFueSBMdGQwgZ8wDQYJ +KoZIhvcNAQEBBQADgY0AMIGJAoGBAM2q22/WNMmn8cC+5EEYGeICySLmp9W6Ay6e +KHr0Xxp3X3epETuPfvAuxp7rOtkS18EMUegkUj8jw0IMEcbyHKFC/rTCaYOt93Cx +GBXMIChiMPAsFeYzGa/D6xzAkfcRaJRQ+Ek3CDLXPnXfo7xpABXezYcPXAJrgsgB +fWrwHdxzAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAtlScqSn4IHFLRiQYQdfOgsPi +wdqD1MPZEniQE0Xp8McZ7kuYbGgdEqzeVgMHqitlzkNNtTz+2u37CbFNXDGCTy5D +2JCgZxaAWNkh1w+4VB91HfMwEU0MqvAO7SB31FwbKNaB3gVnua++NL1cAkujyRny +yR3PatYZCfESQ7oZgds= +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslcertificate/more-certificates/malformed-just-begin-no-newline.pem b/tests/auto/network/ssl/qsslcertificate/more-certificates/malformed-just-begin-no-newline.pem new file mode 100644 index 0000000000..75f3c32241 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/more-certificates/malformed-just-begin-no-newline.pem @@ -0,0 +1 @@ +-----BEGIN CERTIFICATE----- \ No newline at end of file diff --git a/tests/auto/network/ssl/qsslcertificate/more-certificates/malformed-just-begin.pem b/tests/auto/network/ssl/qsslcertificate/more-certificates/malformed-just-begin.pem new file mode 100644 index 0000000000..a71aecf9af --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/more-certificates/malformed-just-begin.pem @@ -0,0 +1 @@ +-----BEGIN CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslcertificate/more-certificates/natwest-banking.pem b/tests/auto/network/ssl/qsslcertificate/more-certificates/natwest-banking.pem new file mode 100644 index 0000000000..c3e303c770 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/more-certificates/natwest-banking.pem @@ -0,0 +1,36 @@ +-----BEGIN CERTIFICATE----- +MIIGTTCCBTWgAwIBAgIQEdaGfQ9bnSLsmQJm4rWlBjANBgkqhkiG9w0BAQUFADCB +vjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2Ug +YXQgaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykwNjE4MDYGA1UEAxMv +VmVyaVNpZ24gQ2xhc3MgMyBFeHRlbmRlZCBWYWxpZGF0aW9uIFNTTCBTR0MgQ0Ew +HhcNMDkwOTE1MDAwMDAwWhcNMTExMTA5MjM1OTU5WjCCAQAxEzARBgsrBgEEAYI3 +PAIBAxMCR0IxGzAZBgNVBA8TElYxLjAsIENsYXVzZSA1LihiKTERMA8GA1UEBRMI +U0MwNDU1NTExCzAJBgNVBAYTAkdCMRAwDgYDVQQRFAdFSDMgNlVZMRAwDgYDVQQI +EwdMb3RoaWFuMRIwEAYDVQQHFAlFZGluYnVyZ2gxFjAUBgNVBAkUDTM0IEZldHRl +cyBSb3cxLTArBgNVBAoUJFRoZSBSb3lhbCBCYW5rIG9mIFNjb3RsYW5kIEdyb3Vw +IFBsYzEVMBMGA1UECxQMV2ViIFNlcnZpY2VzMRYwFAYDVQQDFA13d3cubndvbGIu +Y29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw4bRz9mxrbicnYun +uYoobkfDBjmmJKpSDBbcZCsZDWyLsLvoZh2Ez6Ux9GAbf4m5pLoIQnzQORy40NAt +bNDFhMJV0Iq65ju8qdYaUaWUdrxkLiwrIsiZwUMgcDOwdOvgO4qTev2OjkQg6syj +J+3HTaRrqekfrV5RvBNZ8vPVXK3cWERykzxwqXHwr9QL/n3wqDn4hCOb11Ic2rUf +H9TcPvxv7eFFnGL4ZJ3EU83tE/CmOYgz086BUDBKSvOHqHt2QWiPN/tHAgwfHYKj +eYCMWM21G0rDugeN+urZN+p364kO+VygBxnNIPSr/ZY+4DCdjaKGe8sOJdvI6ip5 +4a1q3QIDAQABo4ICADCCAfwwCQYDVR0TBAIwADAdBgNVHQ4EFgQUX5AjzSTKUsk2 +KfB+nbH+CODuafAwCwYDVR0PBAQDAgWgMD4GA1UdHwQ3MDUwM6AxoC+GLWh0dHA6 +Ly9FVkludGwtY3JsLnZlcmlzaWduLmNvbS9FVkludGwyMDA2LmNybDBEBgNVHSAE +PTA7MDkGC2CGSAGG+EUBBxcGMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LnZl +cmlzaWduLmNvbS9ycGEwNAYDVR0lBC0wKwYIKwYBBQUHAwEGCCsGAQUFBwMCBglg +hkgBhvhCBAEGCisGAQQBgjcKAwMwHwYDVR0jBBgwFoAUTkPIHXbvN1N6T/JYb5Tz +OOLVvd8wdgYIKwYBBQUHAQEEajBoMCsGCCsGAQUFBzABhh9odHRwOi8vRVZJbnRs +LW9jc3AudmVyaXNpZ24uY29tMDkGCCsGAQUFBzAChi1odHRwOi8vRVZJbnRsLWFp +YS52ZXJpc2lnbi5jb20vRVZJbnRsMjAwNi5jZXIwbgYIKwYBBQUHAQwEYjBgoV6g +XDBaMFgwVhYJaW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQUS2u5KJYGDLvQUjibKaxL +B4shBRgwJhYkaHR0cDovL2xvZ28udmVyaXNpZ24uY29tL3ZzbG9nbzEuZ2lmMA0G +CSqGSIb3DQEBBQUAA4IBAQCT64k2YepUu257B3pA4pjbKr/dmlOztUYS/IAtlJpo +As+R+T9dohGP+4liqHtlMMSKPRnwmSCj/KucJJ9UnIC89D3bVAP1Drvk5+gTwGZ5 +JkqPQqZsfEaeihmf8iu9stkYSQxMJcr24S4VEiGt2rdHhESq0OUel4SkMhlmSp5P +sZxqX1HByBJnsF4bcvIY3C4eTrso5awqNgomGhxizJWmK8/sdEcys82SHgSjG4Bp +05gUpXQrLTqWXDDOg5Uy745Gc8TpgqW1ZfpGg7EkjJX7EhBSKF0/2cNKVYUE9bnO +VGd7vK10NOhK7Uk9CkDALK+MvIIkxmRBmAFuQ4D+eqNF +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslcertificate/more-certificates/no-ending-newline.pem b/tests/auto/network/ssl/qsslcertificate/more-certificates/no-ending-newline.pem new file mode 100644 index 0000000000..f8056c72d0 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/more-certificates/no-ending-newline.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB8zCCAVwCAREwDQYJKoZIhvcNAQEFBQAwWzELMAkGA1UEBhMCQVUxEzARBgNV +BAgTClF1ZWVuc2xhbmQxGjAYBgNVBAoTEUNyeXB0U29mdCBQdHkgTHRkMRswGQYD +VQQDExJUZXN0IENBICgxMDI0IGJpdCkwHhcNMDcwNDE3MDc0MDI2WhcNMDcwNTE3 +MDc0MDI2WjApMRowGAYDVQQDExFuYW1lL3dpdGgvc2xhc2hlczELMAkGA1UEBhMC +Tk8wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOud6QOsME+pWANExxgmL0iT +1ayg++hTxHsqAYnm/FoMxfUh+NdKkgJn2/GfNppinfPOSI667VqonU+7JBZDTLV5 +CPbZIo9fFQpDJQN6naev4yaxU1VeYFfI7S8c8zYKeGSR+RenNNeLvfH80YxPpZZ1 +snv8IfDH2V8MVxiyr7lLAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAleaU4pgzV6KY ++q9QuXapUYMsC2GiNtDmkG3k+MTHUO8XlE4hqPrIM6rRf7zKQdZ950R2wL9FSnYl +Qm1Tdv38dCka6ivMBqvRuOt9axH3m0G7nzHL7U3zaCbtEx3yVln+b3yYtiVpTuq0 +3MLrt7tQGAW6ra8ISf6YY1W65/uVXZE= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/tests/auto/network/ssl/qsslcertificate/more-certificates/test-cn-two-cns-cert.pem b/tests/auto/network/ssl/qsslcertificate/more-certificates/test-cn-two-cns-cert.pem new file mode 100644 index 0000000000..08176289d7 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/more-certificates/test-cn-two-cns-cert.pem @@ -0,0 +1,67 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 10 (0xa) + Signature Algorithm: sha1WithRSAEncryption + Issuer: CN=Westpoint Certificate Test CA, ST=Lancashire, C=UK/emailAddress=ca@example.com, O=Westpoint Certificate Test Root Certification Authority + Validity + Not Before: Jul 31 21:01:17 2011 GMT + Not After : Jul 28 21:01:17 2021 GMT + Subject: CN=www.example.com, CN=www2.example.com, ST=Lancashire, C=UK, O=Some organisation + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1024 bit) + Modulus: + 00:e8:61:96:bd:d0:d2:c6:54:92:15:fe:42:2b:86: + 20:dc:74:51:82:c8:b4:5e:5e:c5:3b:01:6e:1e:84: + 8d:4a:8c:6f:d8:63:21:a7:74:16:02:62:c3:84:1f: + ac:83:00:ad:6d:f8:79:d0:48:19:4a:a4:45:bf:24: + 88:db:89:f7:96:80:70:b1:f9:94:46:8d:52:cd:47: + 11:86:74:3b:04:c6:d2:08:3b:4d:70:b0:d4:a2:5a: + ae:54:43:4e:8c:26:05:77:b4:52:8b:bf:43:26:cd: + 6e:b6:04:ca:36:e1:cf:c3:70:52:7b:73:40:1d:1f: + 2a:44:b9:fc:d3:be:77:5a:df + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Authority Information Access: + OCSP - URI:http://ocsp.example.com:8888/ + + Signature Algorithm: sha1WithRSAEncryption + a7:ce:f4:72:aa:54:01:d5:a0:ef:fb:54:a5:79:d0:51:87:27: + 93:37:d7:a8:92:a0:51:a4:56:a6:93:ea:3b:f4:e2:07:68:29: + 2b:59:e6:eb:71:d8:78:0a:b2:e2:df:13:9f:68:f9:6b:d8:42: + c6:ff:3b:e1:2e:01:91:9b:eb:90:7f:da:54:7e:5a:47:ba:40: + 18:fb:a5:f7:8a:3a:b0:05:37:03:a8:b8:23:61:d4:58:37:97: + 87:54:57:62:aa:7f:e6:73:8f:74:f4:3b:57:29:af:bb:f7:bc: + 33:72:d4:f3:02:22:44:3e:70:1f:48:59:7d:6e:a0:af:c4:91: + 58:4c:99:6b:33:28:36:09:59:c7:71:a7:94:ba:0e:af:87:d2: + 47:75:86:c1:ac:2d:ab:89:83:03:a9:97:cb:50:39:c1:66:a3: + a2:92:f6:c8:42:05:5b:d3:e4:13:17:84:10:57:8f:3e:ae:d0: + 5e:f7:15:54:dd:04:61:2f:d6:d4:25:00:d6:6f:02:bb:b9:9f: + ff:ab:d2:a0:32:87:a0:d2:ff:91:29:bf:5d:74:61:d8:86:e5: + 89:29:5e:c0:85:b1:05:e1:79:68:50:fa:21:98:cc:26:09:d1: + 86:18:4d:2e:84:21:33:d1:ff:b5:bf:78:2d:0b:9e:8c:51:82: + 09:9c:03:ec +-----BEGIN CERTIFICATE----- +MIIDXjCCAkagAwIBAgIBCjANBgkqhkiG9w0BAQUFADCBqzEmMCQGA1UEAxMdV2Vz +dHBvaW50IENlcnRpZmljYXRlIFRlc3QgQ0ExEzARBgNVBAgTCkxhbmNhc2hpcmUx +CzAJBgNVBAYTAlVLMR0wGwYJKoZIhvcNAQkBFg5jYUBleGFtcGxlLmNvbTFAMD4G +A1UEChM3V2VzdHBvaW50IENlcnRpZmljYXRlIFRlc3QgUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eTAeFw0xMTA3MzEyMTAxMTdaFw0yMTA3MjgyMTAxMTdaMHMx +GDAWBgNVBAMTD3d3dy5leGFtcGxlLmNvbTEZMBcGA1UEAxMQd3d3Mi5leGFtcGxl +LmNvbTETMBEGA1UECBMKTGFuY2FzaGlyZTELMAkGA1UEBhMCVUsxGjAYBgNVBAoT +EVNvbWUgb3JnYW5pc2F0aW9uMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDo +YZa90NLGVJIV/kIrhiDcdFGCyLReXsU7AW4ehI1KjG/YYyGndBYCYsOEH6yDAK1t ++HnQSBlKpEW/JIjbifeWgHCx+ZRGjVLNRxGGdDsExtIIO01wsNSiWq5UQ06MJgV3 +tFKLv0MmzW62BMo24c/DcFJ7c0AdHypEufzTvnda3wIDAQABo0gwRjAJBgNVHRME +AjAAMDkGCCsGAQUFBwEBBC0wKzApBggrBgEFBQcwAYYdaHR0cDovL29jc3AuZXhh +bXBsZS5jb206ODg4OC8wDQYJKoZIhvcNAQEFBQADggEBAKfO9HKqVAHVoO/7VKV5 +0FGHJ5M316iSoFGkVqaT6jv04gdoKStZ5utx2HgKsuLfE59o+WvYQsb/O+EuAZGb +65B/2lR+Wke6QBj7pfeKOrAFNwOouCNh1Fg3l4dUV2Kqf+Zzj3T0O1cpr7v3vDNy +1PMCIkQ+cB9IWX1uoK/EkVhMmWszKDYJWcdxp5S6Dq+H0kd1hsGsLauJgwOpl8tQ +OcFmo6KS9shCBVvT5BMXhBBXjz6u0F73FVTdBGEv1tQlANZvAru5n/+r0qAyh6DS +/5Epv110YdiG5YkpXsCFsQXheWhQ+iGYzCYJ0YYYTS6EITPR/7W/eC0LnoxRggmc +A+w= +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslcertificate/more-certificates/test-cn-with-drink-cert.pem b/tests/auto/network/ssl/qsslcertificate/more-certificates/test-cn-with-drink-cert.pem new file mode 100644 index 0000000000..ed8823a520 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/more-certificates/test-cn-with-drink-cert.pem @@ -0,0 +1,66 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 11 (0xb) + Signature Algorithm: sha1WithRSAEncryption + Issuer: CN=Westpoint Certificate Test CA, ST=Lancashire, C=UK/emailAddress=ca@example.com, O=Westpoint Certificate Test Root Certification Authority + Validity + Not Before: Jul 31 21:01:17 2011 GMT + Not After : Jul 28 21:01:17 2021 GMT + Subject: CN=example.com/emailAddress=test@example.com/favouriteDrink=tequila + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1024 bit) + Modulus: + 00:b8:2a:88:dd:a3:5b:4a:44:69:03:d5:6e:18:e9: + 9e:55:0d:35:d0:e6:9a:c2:cc:f8:b5:c5:b6:15:00: + a1:24:33:af:e8:fb:93:b9:cc:b9:22:28:ef:69:57: + b0:a2:d0:e3:9e:e5:73:3e:90:55:5b:91:26:0b:c4: + 1a:a6:b6:9d:a2:68:5b:e8:5b:45:3f:21:ff:84:64: + a7:84:bb:61:b7:72:e7:ea:cb:49:66:40:23:be:bf: + f0:8d:60:f2:fb:71:b2:fc:fe:6b:e7:07:94:35:65: + 66:d8:06:90:82:4d:b1:ba:6c:78:f4:42:17:cb:3c: + 0e:10:70:21:a9:31:3b:88:41 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Authority Information Access: + OCSP - URI:http://ocsp.example.com:8888/ + + Signature Algorithm: sha1WithRSAEncryption + 4b:a3:3d:37:e4:fa:61:3b:8c:94:c7:b1:49:55:a6:63:c4:b8: + 13:33:8c:0b:ad:c9:f6:38:d1:a6:d3:5e:7f:10:23:be:d5:4c: + 63:de:15:9b:eb:f1:08:0c:32:95:ff:87:bc:c7:41:c3:17:af: + 73:c9:ac:18:2c:3d:6d:87:9b:41:71:92:e4:57:f0:da:b7:f4: + f3:92:dd:db:a0:b3:82:1a:8d:88:d4:ff:b2:8e:ac:65:6f:b0: + 59:0a:4e:cf:12:cd:fe:ce:35:85:da:13:1c:5e:d0:30:38:8a: + ec:46:eb:d7:a7:87:93:1b:08:a0:28:b0:2d:fc:7d:36:51:2f: + df:6b:67:57:15:b3:a7:cf:dd:55:ee:81:fc:66:fc:a7:22:5f: + e7:86:91:0b:3f:35:56:5f:fa:41:9c:71:06:03:d0:62:d2:3b: + e2:08:ab:af:42:2b:1f:68:9f:17:0a:20:3b:de:a1:fa:0a:44: + a8:67:67:c2:96:7a:ec:fa:92:4d:8f:c8:ba:be:d7:0c:c7:c8: + 6d:9f:b1:6c:c8:1d:e9:b9:5c:30:f3:a2:52:43:e5:43:2a:54: + 24:15:b3:d5:95:af:f8:01:ab:f3:c3:3f:1d:8e:35:58:11:6c: + 12:82:6f:ad:c3:78:c1:cd:43:ff:93:90:25:9f:97:17:36:8e: + 74:28:e8:a9 +-----BEGIN CERTIFICATE----- +MIIDOzCCAiOgAwIBAgIBCzANBgkqhkiG9w0BAQUFADCBqzEmMCQGA1UEAxMdV2Vz +dHBvaW50IENlcnRpZmljYXRlIFRlc3QgQ0ExEzARBgNVBAgTCkxhbmNhc2hpcmUx +CzAJBgNVBAYTAlVLMR0wGwYJKoZIhvcNAQkBFg5jYUBleGFtcGxlLmNvbTFAMD4G +A1UEChM3V2VzdHBvaW50IENlcnRpZmljYXRlIFRlc3QgUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eTAeFw0xMTA3MzEyMTAxMTdaFw0yMTA3MjgyMTAxMTdaMFAx +FDASBgNVBAMTC2V4YW1wbGUuY29tMR8wHQYJKoZIhvcNAQkBFhB0ZXN0QGV4YW1w +bGUuY29tMRcwFQYKCZImiZPyLGQBBRMHdGVxdWlsYTCBnzANBgkqhkiG9w0BAQEF +AAOBjQAwgYkCgYEAuCqI3aNbSkRpA9VuGOmeVQ010Oaawsz4tcW2FQChJDOv6PuT +ucy5IijvaVewotDjnuVzPpBVW5EmC8Qapradomhb6FtFPyH/hGSnhLtht3Ln6stJ +ZkAjvr/wjWDy+3Gy/P5r5weUNWVm2AaQgk2xumx49EIXyzwOEHAhqTE7iEECAwEA +AaNIMEYwCQYDVR0TBAIwADA5BggrBgEFBQcBAQQtMCswKQYIKwYBBQUHMAGGHWh0 +dHA6Ly9vY3NwLmV4YW1wbGUuY29tOjg4ODgvMA0GCSqGSIb3DQEBBQUAA4IBAQBL +oz035PphO4yUx7FJVaZjxLgTM4wLrcn2ONGm015/ECO+1Uxj3hWb6/EIDDKV/4e8 +x0HDF69zyawYLD1th5tBcZLkV/Dat/Tzkt3boLOCGo2I1P+yjqxlb7BZCk7PEs3+ +zjWF2hMcXtAwOIrsRuvXp4eTGwigKLAt/H02US/fa2dXFbOnz91V7oH8ZvynIl/n +hpELPzVWX/pBnHEGA9Bi0jviCKuvQisfaJ8XCiA73qH6CkSoZ2fClnrs+pJNj8i6 +vtcMx8htn7FsyB3puVww86JSQ+VDKlQkFbPVla/4Aavzwz8djjVYEWwSgm+tw3jB +zUP/k5Aln5cXNo50KOip +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslcertificate/more-certificates/trailing-whitespace.pem b/tests/auto/network/ssl/qsslcertificate/more-certificates/trailing-whitespace.pem new file mode 100644 index 0000000000..e48195d15e --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/more-certificates/trailing-whitespace.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB8zCCAVwCAREwDQYJKoZIhvcNAQEFBQAwWzELMAkGA1UEBhMCQVUxEzARBgNV +BAgTClF1ZWVuc2xhbmQxGjAYBgNVBAoTEUNyeXB0U29mdCBQdHkgTHRkMRswGQYD +VQQDExJUZXN0IENBICgxMDI0IGJpdCkwHhcNMDcwNDE3MDc0MDI2WhcNMDcwNTE3 +MDc0MDI2WjApMRowGAYDVQQDExFuYW1lL3dpdGgvc2xhc2hlczELMAkGA1UEBhMC +Tk8wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOud6QOsME+pWANExxgmL0iT +1ayg++hTxHsqAYnm/FoMxfUh+NdKkgJn2/GfNppinfPOSI667VqonU+7JBZDTLV5 +CPbZIo9fFQpDJQN6naev4yaxU1VeYFfI7S8c8zYKeGSR+RenNNeLvfH80YxPpZZ1 +snv8IfDH2V8MVxiyr7lLAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAleaU4pgzV6KY ++q9QuXapUYMsC2GiNtDmkG3k+MTHUO8XlE4hqPrIM6rRf7zKQdZ950R2wL9FSnYl +Qm1Tdv38dCka6ivMBqvRuOt9axH3m0G7nzHL7U3zaCbtEx3yVln+b3yYtiVpTuq0 +3MLrt7tQGAW6ra8ISf6YY1W65/uVXZE= +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslcertificate/qsslcertificate.pro b/tests/auto/network/ssl/qsslcertificate/qsslcertificate.pro new file mode 100644 index 0000000000..05cce8e509 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/qsslcertificate.pro @@ -0,0 +1,29 @@ +load(qttest_p4) + +SOURCES += tst_qsslcertificate.cpp +!wince*:win32:LIBS += -lws2_32 +QT = core network + +TARGET = tst_qsslcertificate + +win32 { + CONFIG(debug, debug|release) { + DESTDIR = debug +} else { + DESTDIR = release + } +} + +wince*|symbian: { + certFiles.files = certificates more-certificates + certFiles.path = . + DEPLOYMENT += certFiles +} + +wince*: { + DEFINES += SRCDIR=\\\".\\\" +} else:!symbian { + DEFINES += SRCDIR=\\\"$$PWD/\\\" +} + +symbian:TARGET.CAPABILITY = NetworkServices ReadUserData diff --git a/tests/auto/network/ssl/qsslcertificate/tst_qsslcertificate.cpp b/tests/auto/network/ssl/qsslcertificate/tst_qsslcertificate.cpp new file mode 100644 index 0000000000..bcd21eefe2 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/tst_qsslcertificate.cpp @@ -0,0 +1,995 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include +#include +#include + +#ifdef Q_OS_SYMBIAN +// In Symbian OS test data is located in applications private dir +// Current path (C:\private\) contains only ascii chars +#define SRCDIR "./" +#endif + +class tst_QSslCertificate : public QObject +{ + Q_OBJECT + + struct CertInfo { + QFileInfo fileInfo; + QFileInfo fileInfo_digest_md5; + QFileInfo fileInfo_digest_sha1; + QSsl::EncodingFormat format; + CertInfo(const QFileInfo &fileInfo, QSsl::EncodingFormat format) + : fileInfo(fileInfo), format(format) {} + }; + + QList certInfoList; + QMap subjAltNameMap; + QMap pubkeyMap; + QMap md5Map; + QMap sha1Map; + + void createTestRows(); +#ifndef QT_NO_OPENSSL + void compareCertificates(const QSslCertificate & cert1, const QSslCertificate & cert2); +#endif + + QString oldCurrentDir; +public: + tst_QSslCertificate(); + virtual ~tst_QSslCertificate(); + +public slots: + void initTestCase_data(); + void init(); + void cleanup(); + +#ifndef QT_NO_OPENSSL +private slots: + void emptyConstructor(); + void constructor_data(); + void constructor(); + void constructingGarbage(); + void copyAndAssign_data(); + void copyAndAssign(); + void digest_data(); + void digest(); + void subjectAlternativeNames_data(); + void utf8SubjectNames(); + void subjectAlternativeNames(); + void publicKey_data(); + void publicKey(); + void toPemOrDer_data(); + void toPemOrDer(); + void fromDevice(); + void fromPath_data(); + void fromPath(); + void certInfo(); + void certInfoQByteArray(); + void task256066toPem(); + void nulInCN(); + void nulInSan(); + void largeSerialNumber(); + void largeExpirationDate(); + void blacklistedCertificates(); + void toText(); + void multipleCommonNames(); + void subjectAndIssuerAttributes(); + void verify(); + + // helper for verbose test failure messages + QString toString(const QList&); + +// ### add tests for certificate bundles (multiple certificates concatenated into a single +// structure); both PEM and DER formatted +#endif +}; + +tst_QSslCertificate::tst_QSslCertificate() +{ + QDir dir(SRCDIR + QLatin1String("/certificates")); + QFileInfoList fileInfoList = dir.entryInfoList(QDir::Files | QDir::Readable); + QRegExp rxCert(QLatin1String("^.+\\.(pem|der)$")); + QRegExp rxSan(QLatin1String("^(.+\\.(?:pem|der))\\.san$")); + QRegExp rxPubKey(QLatin1String("^(.+\\.(?:pem|der))\\.pubkey$")); + QRegExp rxDigest(QLatin1String("^(.+\\.(?:pem|der))\\.digest-(md5|sha1)$")); + foreach (QFileInfo fileInfo, fileInfoList) { + if (rxCert.indexIn(fileInfo.fileName()) >= 0) + certInfoList << + CertInfo(fileInfo, + rxCert.cap(1) == QLatin1String("pem") ? QSsl::Pem : QSsl::Der); + if (rxSan.indexIn(fileInfo.fileName()) >= 0) + subjAltNameMap.insert(rxSan.cap(1), fileInfo.absoluteFilePath()); + if (rxPubKey.indexIn(fileInfo.fileName()) >= 0) + pubkeyMap.insert(rxPubKey.cap(1), fileInfo.absoluteFilePath()); + if (rxDigest.indexIn(fileInfo.fileName()) >= 0) { + if (rxDigest.cap(2) == QLatin1String("md5")) + md5Map.insert(rxDigest.cap(1), fileInfo.absoluteFilePath()); + else + sha1Map.insert(rxDigest.cap(1), fileInfo.absoluteFilePath()); + } + } +} + +tst_QSslCertificate::~tst_QSslCertificate() +{ +} + +void tst_QSslCertificate::initTestCase_data() +{ +} + +void tst_QSslCertificate::init() +{ + QString srcdir(QLatin1String(SRCDIR)); + if (!srcdir.isEmpty()) { + oldCurrentDir = QDir::current().absolutePath(); + QDir::setCurrent(srcdir); + } +} + +void tst_QSslCertificate::cleanup() +{ + if (!oldCurrentDir.isEmpty()) { + QDir::setCurrent(oldCurrentDir); + } + +} + +static QByteArray readFile(const QString &absFilePath) +{ + QFile file(absFilePath); + if (!file.open(QIODevice::ReadOnly)) { + QWARN("failed to open file"); + return QByteArray(); + } + return file.readAll(); +} + +#ifndef QT_NO_OPENSSL + +void tst_QSslCertificate::emptyConstructor() +{ + if (!QSslSocket::supportsSsl()) + return; + + QSslCertificate certificate; + QVERIFY(certificate.isNull()); + //make sure none of the functions crash (task 203035) + QVERIFY(!certificate.isValid()); + QCOMPARE(certificate.version() , QByteArray()); + QCOMPARE(certificate.serialNumber(), QByteArray()); + QCOMPARE(certificate.digest(), QCryptographicHash::hash(QByteArray(), QCryptographicHash::Md5)); + QCOMPARE(certificate.issuerInfo(QSslCertificate::Organization), QStringList()); + QCOMPARE(certificate.subjectInfo(QSslCertificate::Organization), QStringList()); + QCOMPARE(certificate.subjectAlternativeNames(),(QMultiMap())); +#ifndef QT_NO_TEXTSTREAM + QCOMPARE(certificate.effectiveDate(), QDateTime()); + QCOMPARE(certificate.expiryDate(), QDateTime()); +#endif +} + +Q_DECLARE_METATYPE(QSsl::EncodingFormat); + +void tst_QSslCertificate::createTestRows() +{ + QTest::addColumn("absFilePath"); + QTest::addColumn("format"); + foreach (CertInfo certInfo, certInfoList) { + QTest::newRow(certInfo.fileInfo.fileName().toLatin1()) + << certInfo.fileInfo.absoluteFilePath() << certInfo.format; + } +} + +void tst_QSslCertificate::constructor_data() +{ + createTestRows(); +} + +void tst_QSslCertificate::constructor() +{ + if (!QSslSocket::supportsSsl()) + return; + + QFETCH(QString, absFilePath); + QFETCH(QSsl::EncodingFormat, format); + + QByteArray encoded = readFile(absFilePath); + QSslCertificate certificate(encoded, format); + QVERIFY(!certificate.isNull()); +} + +void tst_QSslCertificate::constructingGarbage() +{ + if (!QSslSocket::supportsSsl()) + return; + + QByteArray garbage("garbage"); + QSslCertificate certificate(garbage); + QVERIFY(certificate.isNull()); +} + +void tst_QSslCertificate::copyAndAssign_data() +{ + createTestRows(); +} + +void tst_QSslCertificate::compareCertificates( + const QSslCertificate & cert1, const QSslCertificate & cert2) +{ + QCOMPARE(cert1.isNull(), cert2.isNull()); + // Note: in theory, the next line could fail even if the certificates are identical! + QCOMPARE(cert1.isValid(), cert2.isValid()); + QCOMPARE(cert1.version(), cert2.version()); + QCOMPARE(cert1.serialNumber(), cert2.serialNumber()); + QCOMPARE(cert1.digest(), cert2.digest()); + QCOMPARE(cert1.toPem(), cert2.toPem()); + QCOMPARE(cert1.toDer(), cert2.toDer()); + for (int info = QSslCertificate::Organization; + info <= QSslCertificate::StateOrProvinceName; info++) { + const QSslCertificate::SubjectInfo subjectInfo = (QSslCertificate::SubjectInfo)info; + QCOMPARE(cert1.issuerInfo(subjectInfo), cert2.issuerInfo(subjectInfo)); + QCOMPARE(cert1.subjectInfo(subjectInfo), cert2.subjectInfo(subjectInfo)); + } + QCOMPARE(cert1.subjectAlternativeNames(), cert2.subjectAlternativeNames()); + QCOMPARE(cert1.effectiveDate(), cert2.effectiveDate()); + QCOMPARE(cert1.expiryDate(), cert2.expiryDate()); + QCOMPARE(cert1.version(), cert2.version()); + QCOMPARE(cert1.serialNumber(), cert2.serialNumber()); + // ### add more functions here ... +} + +void tst_QSslCertificate::copyAndAssign() +{ + if (!QSslSocket::supportsSsl()) + return; + + QFETCH(QString, absFilePath); + QFETCH(QSsl::EncodingFormat, format); + + QByteArray encoded = readFile(absFilePath); + QSslCertificate certificate(encoded, format); + + QVERIFY(!certificate.isNull()); + + QSslCertificate copied(certificate); + compareCertificates(certificate, copied); + + QSslCertificate assigned = certificate; + compareCertificates(certificate, assigned); +} + +void tst_QSslCertificate::digest_data() +{ + QTest::addColumn("absFilePath"); + QTest::addColumn("format"); + QTest::addColumn("absFilePath_digest_md5"); + QTest::addColumn("absFilePath_digest_sha1"); + foreach (CertInfo certInfo, certInfoList) { + QString certName = certInfo.fileInfo.fileName(); + QTest::newRow(certName.toLatin1()) + << certInfo.fileInfo.absoluteFilePath() + << certInfo.format + << md5Map.value(certName) + << sha1Map.value(certName); + } +} + +// Converts a digest of the form '{MD5|SHA1} Fingerprint=AB:B8:32...' to binary format. +static QByteArray convertDigest(const QByteArray &input) +{ + QByteArray result; + QRegExp rx(QLatin1String("(?:=|:)([0-9A-Fa-f]{2})")); + int pos = 0; + while ((pos = rx.indexIn(input, pos)) != -1) { + result.append(rx.cap(1).toLatin1()); + pos += rx.matchedLength(); + } + return QByteArray::fromHex(result); +} + +void tst_QSslCertificate::digest() +{ + if (!QSslSocket::supportsSsl()) + return; + + QFETCH(QString, absFilePath); + QFETCH(QSsl::EncodingFormat, format); + QFETCH(QString, absFilePath_digest_md5); + QFETCH(QString, absFilePath_digest_sha1); + + QByteArray encoded = readFile(absFilePath); + QSslCertificate certificate(encoded, format); + QVERIFY(!certificate.isNull()); + + if (!absFilePath_digest_md5.isEmpty()) + QCOMPARE(convertDigest(readFile(absFilePath_digest_md5)), + certificate.digest(QCryptographicHash::Md5)); + + if (!absFilePath_digest_sha1.isEmpty()) + QCOMPARE(convertDigest(readFile(absFilePath_digest_sha1)), + certificate.digest(QCryptographicHash::Sha1)); +} + +void tst_QSslCertificate::subjectAlternativeNames_data() +{ + QTest::addColumn("certFilePath"); + QTest::addColumn("format"); + QTest::addColumn("subjAltNameFilePath"); + + foreach (CertInfo certInfo, certInfoList) { + QString certName = certInfo.fileInfo.fileName(); + if (subjAltNameMap.contains(certName)) + QTest::newRow(certName.toLatin1()) + << certInfo.fileInfo.absoluteFilePath() + << certInfo.format + << subjAltNameMap.value(certName); + } +} + +void tst_QSslCertificate::subjectAlternativeNames() +{ + if (!QSslSocket::supportsSsl()) + return; + + QFETCH(QString, certFilePath); + QFETCH(QSsl::EncodingFormat, format); + QFETCH(QString, subjAltNameFilePath); + + QByteArray encodedCert = readFile(certFilePath); + QSslCertificate certificate(encodedCert, format); + QVERIFY(!certificate.isNull()); + + QByteArray fileContents = readFile(subjAltNameFilePath); + + const QMultiMap altSubjectNames = + certificate.subjectAlternativeNames(); + + // verify that each entry in subjAltNames is present in fileContents + QMapIterator it(altSubjectNames); + while (it.hasNext()) { + it.next(); + QString type; + if (it.key() == QSsl::EmailEntry) + type = QLatin1String("email"); + else if (it.key() == QSsl::DnsEntry) + type = QLatin1String("DNS"); + else + QFAIL("unsupported alternative name type"); + QString entry = QString("%1:%2").arg(type).arg(it.value()); + QVERIFY(fileContents.contains(entry.toAscii())); + } + + // verify that each entry in fileContents is present in subjAltNames + QRegExp rx(QLatin1String("(email|DNS):([^,\\r\\n]+)")); + for (int pos = 0; (pos = rx.indexIn(fileContents, pos)) != -1; pos += rx.matchedLength()) { + QSsl::AlternativeNameEntryType key; + if (rx.cap(1) == QLatin1String("email")) + key = QSsl::EmailEntry; + else if (rx.cap(1) == QLatin1String("DNS")) + key = QSsl::DnsEntry; + else + QFAIL("unsupported alternative name type"); + QVERIFY(altSubjectNames.contains(key, rx.cap(2))); + } +} + +void tst_QSslCertificate::utf8SubjectNames() +{ + QSslCertificate cert = QSslCertificate::fromPath("certificates/cert-ss-san-utf8.pem", QSsl::Pem, + QRegExp::FixedString).first(); + QVERIFY(!cert.isNull()); + + // O is "Heavy Metal Records" with heavy use of "decorations" like accents, umlauts etc., + // OU uses arabian / asian script letters near codepoint 64K. + // strings split where the compiler would otherwise find three-digit hex numbers + static const char *o = "H\xc4\x95\xc4\x82\xc6\xb2\xc3\xbf \xca\x8d\xe1\xba\xbf\xca\x88\xe1\xba" + "\xb7\xe1\xb8\xbb R\xc3\xa9" "c" "\xc3\xb6rd\xc5\x9d"; + static const char *ou = "\xe3\x88\xa7" "A" "\xe3\x89\x81\xef\xbd\xab" "BC"; + + // the following two tests should help find "\x"-literal encoding bugs in the test itself + QCOMPARE(cert.subjectInfo("O")[0].length(), QString::fromUtf8(o).length()); + QCOMPARE (cert.subjectInfo("O")[0].toUtf8().toHex(), QByteArray(o).toHex()); + + QCOMPARE(cert.subjectInfo("O")[0], QString::fromUtf8(o)); + QCOMPARE(cert.subjectInfo("OU")[0], QString::fromUtf8(ou)); +} + +void tst_QSslCertificate::publicKey_data() +{ + QTest::addColumn("certFilePath"); + QTest::addColumn("format"); + QTest::addColumn("pubkeyFilePath"); + + foreach (CertInfo certInfo, certInfoList) { + QString certName = certInfo.fileInfo.fileName(); + if (pubkeyMap.contains(certName)) + QTest::newRow(certName.toLatin1()) + << certInfo.fileInfo.absoluteFilePath() + << certInfo.format + << pubkeyMap.value(certName); + } +} + +void tst_QSslCertificate::publicKey() +{ + if (!QSslSocket::supportsSsl()) + return; + + QFETCH(QString, certFilePath); + QFETCH(QSsl::EncodingFormat, format); + QFETCH(QString, pubkeyFilePath); + + QByteArray encodedCert = readFile(certFilePath); + QSslCertificate certificate(encodedCert, format); + QVERIFY(!certificate.isNull()); + + QByteArray encodedPubkey = readFile(pubkeyFilePath); + QSslKey pubkey(encodedPubkey, QSsl::Rsa, format, QSsl::PublicKey); // ### support DSA as well! + QVERIFY(!pubkey.isNull()); + + QCOMPARE(certificate.publicKey(), pubkey); +} + +void tst_QSslCertificate::toPemOrDer_data() +{ + createTestRows(); +} + +static const char BeginCertString[] = "-----BEGIN CERTIFICATE-----"; +static const char EndCertString[] = "-----END CERTIFICATE-----"; + +// Returns, in Pem-format, the first certificate found in a Pem-formatted block +// (Note that such a block may contain e.g. a private key at the end). +static QByteArray firstPemCertificateFromPem(const QByteArray &pem) +{ + int startPos = pem.indexOf(BeginCertString); + int endPos = pem.indexOf(EndCertString); + if (startPos == -1 || endPos == -1) + return QByteArray(); + return pem.mid(startPos, endPos + sizeof(EndCertString) - startPos); +} + +void tst_QSslCertificate::toPemOrDer() +{ + if (!QSslSocket::supportsSsl()) + return; + + QFETCH(QString, absFilePath); + QFETCH(QSsl::EncodingFormat, format); + + QByteArray encoded = readFile(absFilePath); + QSslCertificate certificate(encoded, format); + QVERIFY(!certificate.isNull()); + if (format == QSsl::Pem) { + encoded.replace('\r',""); + QByteArray firstPem = firstPemCertificateFromPem(encoded); + QCOMPARE(certificate.toPem(), firstPem); + } else { + // ### for now, we assume that DER-encoded certificates don't contain bundled stuff + QCOMPARE(certificate.toDer(), encoded); + } +} + +void tst_QSslCertificate::fromDevice() +{ + QTest::ignoreMessage(QtWarningMsg, "QSslCertificate::fromDevice: cannot read from a null device"); + QList certs = QSslCertificate::fromDevice(0); // don't crash + QVERIFY(certs.isEmpty()); +} + +void tst_QSslCertificate::fromPath_data() +{ + QTest::addColumn("path"); + QTest::addColumn("syntax"); + QTest::addColumn("pemencoding"); + QTest::addColumn("numCerts"); + + QTest::newRow("empty fixed pem") << QString() << int(QRegExp::FixedString) << true << 0; + QTest::newRow("empty fixed der") << QString() << int(QRegExp::FixedString) << false << 0; + QTest::newRow("empty regexp pem") << QString() << int(QRegExp::RegExp) << true << 0; + QTest::newRow("empty regexp der") << QString() << int(QRegExp::RegExp) << false << 0; + QTest::newRow("empty wildcard pem") << QString() << int(QRegExp::Wildcard) << true << 0; + QTest::newRow("empty wildcard der") << QString() << int(QRegExp::Wildcard) << false << 0; + QTest::newRow("\"certificates\" fixed pem") << QString("certificates") << int(QRegExp::FixedString) << true << 0; + QTest::newRow("\"certificates\" fixed der") << QString("certificates") << int(QRegExp::FixedString) << false << 0; + QTest::newRow("\"certificates\" regexp pem") << QString("certificates") << int(QRegExp::RegExp) << true << 0; + QTest::newRow("\"certificates\" regexp der") << QString("certificates") << int(QRegExp::RegExp) << false << 0; + QTest::newRow("\"certificates\" wildcard pem") << QString("certificates") << int(QRegExp::Wildcard) << true << 0; + QTest::newRow("\"certificates\" wildcard der") << QString("certificates") << int(QRegExp::Wildcard) << false << 0; + QTest::newRow("\"certificates/cert.pem\" fixed pem") << QString("certificates/cert.pem") << int(QRegExp::FixedString) << true << 1; + QTest::newRow("\"certificates/cert.pem\" fixed der") << QString("certificates/cert.pem") << int(QRegExp::FixedString) << false << 0; + QTest::newRow("\"certificates/cert.pem\" regexp pem") << QString("certificates/cert.pem") << int(QRegExp::RegExp) << true << 1; + QTest::newRow("\"certificates/cert.pem\" regexp der") << QString("certificates/cert.pem") << int(QRegExp::RegExp) << false << 0; + QTest::newRow("\"certificates/cert.pem\" wildcard pem") << QString("certificates/cert.pem") << int(QRegExp::Wildcard) << true << 1; + QTest::newRow("\"certificates/cert.pem\" wildcard der") << QString("certificates/cert.pem") << int(QRegExp::Wildcard) << false << 0; + QTest::newRow("\"certificates/*\" fixed pem") << QString("certificates/*") << int(QRegExp::FixedString) << true << 0; + QTest::newRow("\"certificates/*\" fixed der") << QString("certificates/*") << int(QRegExp::FixedString) << false << 0; + QTest::newRow("\"certificates/*\" regexp pem") << QString("certificates/*") << int(QRegExp::RegExp) << true << 0; + QTest::newRow("\"certificates/*\" regexp der") << QString("certificates/*") << int(QRegExp::RegExp) << false << 0; + QTest::newRow("\"certificates/*\" wildcard pem") << QString("certificates/*") << int(QRegExp::Wildcard) << true << 5; + QTest::newRow("\"certificates/*\" wildcard der") << QString("certificates/*") << int(QRegExp::Wildcard) << false << 0; + QTest::newRow("\"c*/c*.pem\" fixed pem") << QString("c*/c*.pem") << int(QRegExp::FixedString) << true << 0; + QTest::newRow("\"c*/c*.pem\" fixed der") << QString("c*/c*.pem") << int(QRegExp::FixedString) << false << 0; + QTest::newRow("\"c*/c*.pem\" regexp pem") << QString("c*/c*.pem") << int(QRegExp::RegExp) << true << 0; + QTest::newRow("\"c*/c*.pem\" regexp der") << QString("c*/c*.pem") << int(QRegExp::RegExp) << false << 0; + QTest::newRow("\"c*/c*.pem\" wildcard pem") << QString("c*/c*.pem") << int(QRegExp::Wildcard) << true << 5; + QTest::newRow("\"c*/c*.pem\" wildcard der") << QString("c*/c*.pem") << int(QRegExp::Wildcard) << false << 0; + QTest::newRow("\"d*/c*.pem\" fixed pem") << QString("d*/c*.pem") << int(QRegExp::FixedString) << true << 0; + QTest::newRow("\"d*/c*.pem\" fixed der") << QString("d*/c*.pem") << int(QRegExp::FixedString) << false << 0; + QTest::newRow("\"d*/c*.pem\" regexp pem") << QString("d*/c*.pem") << int(QRegExp::RegExp) << true << 0; + QTest::newRow("\"d*/c*.pem\" regexp der") << QString("d*/c*.pem") << int(QRegExp::RegExp) << false << 0; + QTest::newRow("\"d*/c*.pem\" wildcard pem") << QString("d*/c*.pem") << int(QRegExp::Wildcard) << true << 0; + QTest::newRow("\"d*/c*.pem\" wildcard der") << QString("d*/c*.pem") << int(QRegExp::Wildcard) << false << 0; + QTest::newRow("\"c.*/c.*.pem\" fixed pem") << QString("c.*/c.*.pem") << int(QRegExp::FixedString) << true << 0; + QTest::newRow("\"c.*/c.*.pem\" fixed der") << QString("c.*/c.*.pem") << int(QRegExp::FixedString) << false << 0; + QTest::newRow("\"c.*/c.*.pem\" regexp pem") << QString("c.*/c.*.pem") << int(QRegExp::RegExp) << true << 5; + QTest::newRow("\"c.*/c.*.pem\" regexp der") << QString("c.*/c.*.pem") << int(QRegExp::RegExp) << false << 0; + QTest::newRow("\"c.*/c.*.pem\" wildcard pem") << QString("c.*/c.*.pem") << int(QRegExp::Wildcard) << true << 0; + QTest::newRow("\"c.*/c.*.pem\" wildcard der") << QString("c.*/c.*.pem") << int(QRegExp::Wildcard) << false << 0; + QTest::newRow("\"d.*/c.*.pem\" fixed pem") << QString("d.*/c.*.pem") << int(QRegExp::FixedString) << true << 0; + QTest::newRow("\"d.*/c.*.pem\" fixed der") << QString("d.*/c.*.pem") << int(QRegExp::FixedString) << false << 0; + QTest::newRow("\"d.*/c.*.pem\" regexp pem") << QString("d.*/c.*.pem") << int(QRegExp::RegExp) << true << 0; + QTest::newRow("\"d.*/c.*.pem\" regexp der") << QString("d.*/c.*.pem") << int(QRegExp::RegExp) << false << 0; + QTest::newRow("\"d.*/c.*.pem\" wildcard pem") << QString("d.*/c.*.pem") << int(QRegExp::Wildcard) << true << 0; + QTest::newRow("\"d.*/c.*.pem\" wildcard der") << QString("d.*/c.*.pem") << int(QRegExp::Wildcard) << false << 0; +#ifdef Q_OS_LINUX + QTest::newRow("absolute path wildcard pem") << QString(QDir::currentPath() + "/certificates/*.pem") << int(QRegExp::Wildcard) << true << 5; +#endif + + QTest::newRow("trailing-whitespace") << QString("more-certificates/trailing-whitespace.pem") << int(QRegExp::FixedString) << true << 1; + QTest::newRow("no-ending-newline") << QString("more-certificates/no-ending-newline.pem") << int(QRegExp::FixedString) << true << 1; + QTest::newRow("malformed-just-begin") << QString("more-certificates/malformed-just-begin.pem") << int(QRegExp::FixedString) << true << 0; + QTest::newRow("malformed-just-begin-no-newline") << QString("more-certificates/malformed-just-begin-no-newline.pem") << int(QRegExp::FixedString) << true << 0; +} + +void tst_QSslCertificate::fromPath() +{ + QFETCH(QString, path); + QFETCH(int, syntax); + QFETCH(bool, pemencoding); + QFETCH(int, numCerts); + + QCOMPARE(QSslCertificate::fromPath(path, + pemencoding ? QSsl::Pem : QSsl::Der, + QRegExp::PatternSyntax(syntax)).size(), + numCerts); +} + +void tst_QSslCertificate::certInfo() +{ +// MD5 Fingerprint=B6:CF:57:34:DA:A9:73:21:82:F7:CF:4D:3D:85:31:88 +// SHA1 Fingerprint=B6:D1:51:82:E0:29:CA:59:96:38:BD:B6:F9:40:05:91:6D:49:09:60 +// Certificate: +// Data: +// Version: 1 (0x0) +// Serial Number: 17 (0x11) +// Signature Algorithm: sha1WithRSAEncryption +// Issuer: C=AU, ST=Queensland, O=CryptSoft Pty Ltd, CN=Test CA (1024 bit) +// Validity +// Not Before: Apr 17 07:40:26 2007 GMT +// Not After : May 17 07:40:26 2007 GMT +// Subject: CN=name/with/slashes, C=NO +// Subject Public Key Info: +// Public Key Algorithm: rsaEncryption +// RSA Public Key: (1024 bit) +// Modulus (1024 bit): +// 00:eb:9d:e9:03:ac:30:4f:a9:58:03:44:c7:18:26: +// 2f:48:93:d5:ac:a0:fb:e8:53:c4:7b:2a:01:89:e6: +// fc:5a:0c:c5:f5:21:f8:d7:4a:92:02:67:db:f1:9f: +// 36:9a:62:9d:f3:ce:48:8e:ba:ed:5a:a8:9d:4f:bb: +// 24:16:43:4c:b5:79:08:f6:d9:22:8f:5f:15:0a:43: +// 25:03:7a:9d:a7:af:e3:26:b1:53:55:5e:60:57:c8: +// ed:2f:1c:f3:36:0a:78:64:91:f9:17:a7:34:d7:8b: +// bd:f1:fc:d1:8c:4f:a5:96:75:b2:7b:fc:21:f0:c7: +// d9:5f:0c:57:18:b2:af:b9:4b +// Exponent: 65537 (0x10001) +// Signature Algorithm: sha1WithRSAEncryption +// 95:e6:94:e2:98:33:57:a2:98:fa:af:50:b9:76:a9:51:83:2c: +// 0b:61:a2:36:d0:e6:90:6d:e4:f8:c4:c7:50:ef:17:94:4e:21: +// a8:fa:c8:33:aa:d1:7f:bc:ca:41:d6:7d:e7:44:76:c0:bf:45: +// 4a:76:25:42:6d:53:76:fd:fc:74:29:1a:ea:2b:cc:06:ab:d1: +// b8:eb:7d:6b:11:f7:9b:41:bb:9f:31:cb:ed:4d:f3:68:26:ed: +// 13:1d:f2:56:59:fe:6f:7c:98:b6:25:69:4e:ea:b4:dc:c2:eb: +// b7:bb:50:18:05:ba:ad:af:08:49:fe:98:63:55:ba:e7:fb:95: +// 5d:91 + static const char pem[] = + "-----BEGIN CERTIFICATE-----\n" + "MIIB8zCCAVwCAREwDQYJKoZIhvcNAQEFBQAwWzELMAkGA1UEBhMCQVUxEzARBgNV\n" + "BAgTClF1ZWVuc2xhbmQxGjAYBgNVBAoTEUNyeXB0U29mdCBQdHkgTHRkMRswGQYD\n" + "VQQDExJUZXN0IENBICgxMDI0IGJpdCkwHhcNMDcwNDE3MDc0MDI2WhcNMDcwNTE3\n" + "MDc0MDI2WjApMRowGAYDVQQDExFuYW1lL3dpdGgvc2xhc2hlczELMAkGA1UEBhMC\n" + "Tk8wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOud6QOsME+pWANExxgmL0iT\n" + "1ayg++hTxHsqAYnm/FoMxfUh+NdKkgJn2/GfNppinfPOSI667VqonU+7JBZDTLV5\n" + "CPbZIo9fFQpDJQN6naev4yaxU1VeYFfI7S8c8zYKeGSR+RenNNeLvfH80YxPpZZ1\n" + "snv8IfDH2V8MVxiyr7lLAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAleaU4pgzV6KY\n" + "+q9QuXapUYMsC2GiNtDmkG3k+MTHUO8XlE4hqPrIM6rRf7zKQdZ950R2wL9FSnYl\n" + "Qm1Tdv38dCka6ivMBqvRuOt9axH3m0G7nzHL7U3zaCbtEx3yVln+b3yYtiVpTuq0\n" + "3MLrt7tQGAW6ra8ISf6YY1W65/uVXZE=\n" + "-----END CERTIFICATE-----\n"; + static const char der[] = // hex encoded + "30:82:01:f3:30:82:01:5c:02:01:11:30:0d:06:09:2a" + "86:48:86:f7:0d:01:01:05:05:00:30:5b:31:0b:30:09" + "06:03:55:04:06:13:02:41:55:31:13:30:11:06:03:55" + "04:08:13:0a:51:75:65:65:6e:73:6c:61:6e:64:31:1a" + "30:18:06:03:55:04:0a:13:11:43:72:79:70:74:53:6f" + "66:74:20:50:74:79:20:4c:74:64:31:1b:30:19:06:03" + "55:04:03:13:12:54:65:73:74:20:43:41:20:28:31:30" + "32:34:20:62:69:74:29:30:1e:17:0d:30:37:30:34:31" + "37:30:37:34:30:32:36:5a:17:0d:30:37:30:35:31:37" + "30:37:34:30:32:36:5a:30:29:31:1a:30:18:06:03:55" + "04:03:13:11:6e:61:6d:65:2f:77:69:74:68:2f:73:6c" + "61:73:68:65:73:31:0b:30:09:06:03:55:04:06:13:02" + "4e:4f:30:81:9f:30:0d:06:09:2a:86:48:86:f7:0d:01" + "01:01:05:00:03:81:8d:00:30:81:89:02:81:81:00:eb" + "9d:e9:03:ac:30:4f:a9:58:03:44:c7:18:26:2f:48:93" + "d5:ac:a0:fb:e8:53:c4:7b:2a:01:89:e6:fc:5a:0c:c5" + "f5:21:f8:d7:4a:92:02:67:db:f1:9f:36:9a:62:9d:f3" + "ce:48:8e:ba:ed:5a:a8:9d:4f:bb:24:16:43:4c:b5:79" + "08:f6:d9:22:8f:5f:15:0a:43:25:03:7a:9d:a7:af:e3" + "26:b1:53:55:5e:60:57:c8:ed:2f:1c:f3:36:0a:78:64" + "91:f9:17:a7:34:d7:8b:bd:f1:fc:d1:8c:4f:a5:96:75" + "b2:7b:fc:21:f0:c7:d9:5f:0c:57:18:b2:af:b9:4b:02" + "03:01:00:01:30:0d:06:09:2a:86:48:86:f7:0d:01:01" + "05:05:00:03:81:81:00:95:e6:94:e2:98:33:57:a2:98" + "fa:af:50:b9:76:a9:51:83:2c:0b:61:a2:36:d0:e6:90" + "6d:e4:f8:c4:c7:50:ef:17:94:4e:21:a8:fa:c8:33:aa" + "d1:7f:bc:ca:41:d6:7d:e7:44:76:c0:bf:45:4a:76:25" + "42:6d:53:76:fd:fc:74:29:1a:ea:2b:cc:06:ab:d1:b8" + "eb:7d:6b:11:f7:9b:41:bb:9f:31:cb:ed:4d:f3:68:26" + "ed:13:1d:f2:56:59:fe:6f:7c:98:b6:25:69:4e:ea:b4" + "dc:c2:eb:b7:bb:50:18:05:ba:ad:af:08:49:fe:98:63" + "55:ba:e7:fb:95:5d:91"; + + QSslCertificate cert = QSslCertificate::fromPath("certificates/cert.pem", QSsl::Pem, + QRegExp::FixedString).first(); + QVERIFY(!cert.isNull()); + + QCOMPARE(cert.issuerInfo(QSslCertificate::Organization)[0], QString("CryptSoft Pty Ltd")); + QCOMPARE(cert.issuerInfo(QSslCertificate::CommonName)[0], QString("Test CA (1024 bit)")); + QCOMPARE(cert.issuerInfo(QSslCertificate::LocalityName), QStringList()); + QCOMPARE(cert.issuerInfo(QSslCertificate::OrganizationalUnitName), QStringList()); + QCOMPARE(cert.issuerInfo(QSslCertificate::CountryName)[0], QString("AU")); + QCOMPARE(cert.issuerInfo(QSslCertificate::StateOrProvinceName)[0], QString("Queensland")); + + QCOMPARE(cert.issuerInfo("O")[0], QString("CryptSoft Pty Ltd")); + QCOMPARE(cert.issuerInfo("CN")[0], QString("Test CA (1024 bit)")); + QCOMPARE(cert.issuerInfo("L"), QStringList()); + QCOMPARE(cert.issuerInfo("OU"), QStringList()); + QCOMPARE(cert.issuerInfo("C")[0], QString("AU")); + QCOMPARE(cert.issuerInfo("ST")[0], QString("Queensland")); + + QCOMPARE(cert.subjectInfo(QSslCertificate::Organization), QStringList()); + QCOMPARE(cert.subjectInfo(QSslCertificate::CommonName)[0], QString("name/with/slashes")); + QCOMPARE(cert.subjectInfo(QSslCertificate::LocalityName), QStringList()); + QCOMPARE(cert.subjectInfo(QSslCertificate::OrganizationalUnitName), QStringList()); + QCOMPARE(cert.subjectInfo(QSslCertificate::CountryName)[0], QString("NO")); + QCOMPARE(cert.subjectInfo(QSslCertificate::StateOrProvinceName), QStringList()); + + QCOMPARE(cert.subjectInfo("O"), QStringList()); + QCOMPARE(cert.subjectInfo("CN")[0], QString("name/with/slashes")); + QCOMPARE(cert.subjectInfo("L"), QStringList()); + QCOMPARE(cert.subjectInfo("OU"), QStringList()); + QCOMPARE(cert.subjectInfo("C")[0], QString("NO")); + QCOMPARE(cert.subjectInfo("ST"), QStringList()); + + QCOMPARE(cert.version(), QByteArray::number(1)); + QCOMPARE(cert.serialNumber(), QByteArray::number(17)); + + QCOMPARE(cert.toPem().constData(), (const char*)pem); + QCOMPARE(cert.toDer(), QByteArray::fromHex(der)); + + QCOMPARE(cert.digest(QCryptographicHash::Md5), + QByteArray::fromHex("B6:CF:57:34:DA:A9:73:21:82:F7:CF:4D:3D:85:31:88")); + QCOMPARE(cert.digest(QCryptographicHash::Sha1), + QByteArray::fromHex("B6:D1:51:82:E0:29:CA:59:96:38:BD:B6:F9:40:05:91:6D:49:09:60")); + + QCOMPARE(cert.effectiveDate().toUTC(), QDateTime(QDate(2007, 4, 17), QTime(7,40,26), Qt::UTC)); + QCOMPARE(cert.expiryDate().toUTC(), QDateTime(QDate(2007, 5, 17), QTime(7,40,26), Qt::UTC)); + QVERIFY(!cert.isValid()); // cert has expired + + QSslCertificate copy = cert; + QVERIFY(cert == copy); + QVERIFY(!(cert != copy)); + + QCOMPARE(cert, QSslCertificate(pem, QSsl::Pem)); + QCOMPARE(cert, QSslCertificate(QByteArray::fromHex(der), QSsl::Der)); +} + +void tst_QSslCertificate::certInfoQByteArray() +{ + QSslCertificate cert = QSslCertificate::fromPath("certificates/cert.pem", QSsl::Pem, + QRegExp::FixedString).first(); + QVERIFY(!cert.isNull()); + + // in this test, check the bytearray variants before the enum variants to see if + // we fixed a bug we had with lazy initialization of the values. + QCOMPARE(cert.issuerInfo("CN")[0], QString("Test CA (1024 bit)")); + QCOMPARE(cert.subjectInfo("CN")[0], QString("name/with/slashes")); +} + +void tst_QSslCertificate::task256066toPem() +{ + // a certificate whose PEM encoding's length is a multiple of 64 + const char *mycert = "-----BEGIN CERTIFICATE-----\n" \ + "MIIEGjCCAwKgAwIBAgIESikYSjANBgkqhkiG9w0BAQUFADBbMQswCQYDVQQGEwJF\n" \ + "RTEiMCAGA1UEChMZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1czEPMA0GA1UECxMG\n" \ + "RVNURUlEMRcwFQYDVQQDEw5FU1RFSUQtU0sgMjAwNzAeFw0wOTA2MDUxMzA2MTha\n" \ + "Fw0xNDA2MDkyMTAwMDBaMIGRMQswCQYDVQQGEwJFRTEPMA0GA1UEChMGRVNURUlE\n" \ + "MRcwFQYDVQQLEw5hdXRoZW50aWNhdGlvbjEhMB8GA1UEAxMYSEVJQkVSRyxTVkVO\n" \ + "LDM3NzA5MjcwMjg1MRAwDgYDVQQEEwdIRUlCRVJHMQ0wCwYDVQQqEwRTVkVOMRQw\n" \ + "EgYDVQQFEwszNzcwOTI3MDI4NTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA\n" \ + "k2Euwhm34vu1jOFp02J5fQRx9LW2C7x78CbJ7yInoAKn7QR8UdxTU7mJk90Opejo\n" \ + "71RUi2/aYl4jCr9gr99v2YoLufMRwAuqdmwmwqH1WAHRUtIcD0oPdKyelmmn9ig0\n" \ + "RV+yJLNT3dnyrwPw+uuzDe3DeKepGKE4lxexliCaAx0CAyCMW6OCATEwggEtMA4G\n" \ + "A1UdDwEB/wQEAwIEsDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwPAYD\n" \ + "VR0fBDUwMzAxoC+gLYYraHR0cDovL3d3dy5zay5lZS9jcmxzL2VzdGVpZC9lc3Rl\n" \ + "aWQyMDA3LmNybDAgBgNVHREEGTAXgRVzdmVuLmhlaWJlcmdAZWVzdGkuZWUwUQYD\n" \ + "VR0gBEowSDBGBgsrBgEEAc4fAQEBATA3MBIGCCsGAQUFBwICMAYaBG5vbmUwIQYI\n" \ + "KwYBBQUHAgEWFWh0dHA6Ly93d3cuc2suZWUvY3BzLzAfBgNVHSMEGDAWgBRIBt6+\n" \ + "jIdXlYB4Y/qcIysroDoYdTAdBgNVHQ4EFgQUKCjpDf+LcvL6AH0QOiW6rMTtB/0w\n" \ + "CQYDVR0TBAIwADANBgkqhkiG9w0BAQUFAAOCAQEABRyRuUm9zt8V27WuNeXtCDmU\n" \ + "MGzA6g4QXNAd2nxFzT3k+kNzzQTOcgRdmjiEPuK49On+GWnBr/5MSBNhbCJVPWr/\n" \ + "yym1UYTBisaqhRt/N/kwZqd0bHeLJk+ZxSePXRyqkp9H8KPWqz7H+O/FxRS4ffxo\n" \ + "Q9Clem+e0bcjNlL5xXiRGycBeZq8cKj+0+A/UuattznQlvHdlCEsSeu1fPOORqFV\n" \ + "fZur4HC31lQD7xVvETLiL83CtOQC78+29XPD6Zlrrc5OF2yibSVParY19b8Zh6yu\n" \ + "p1dNvN8pBgXGrsyxRonwHooV2ghGNmGILkpdvlQfnxeCUg4erfHjDdSY9vmT7w==\n" \ + "-----END CERTIFICATE-----\n"; + + QByteArray pem1(mycert); + QSslCertificate cert1(pem1); + QVERIFY(!cert1.isNull()); + QByteArray pem2(cert1.toPem()); + QSslCertificate cert2(pem2); + QVERIFY(!cert2.isNull()); + QCOMPARE(pem1, pem2); +} + +void tst_QSslCertificate::nulInCN() +{ + QList certList = + QSslCertificate::fromPath(SRCDIR "more-certificates/badguy-nul-cn.crt"); + QCOMPARE(certList.size(), 1); + + const QSslCertificate &cert = certList.at(0); + QVERIFY(!cert.isNull()); + + QString cn = cert.subjectInfo(QSslCertificate::CommonName)[0]; + QVERIFY(cn != "www.bank.com"); + + static const char realCN[] = "www.bank.com\0.badguy.com"; + QCOMPARE(cn, QString::fromLatin1(realCN, sizeof realCN - 1)); +} + +void tst_QSslCertificate::nulInSan() +{ + QList certList = + QSslCertificate::fromPath(SRCDIR "more-certificates/badguy-nul-san.crt"); + QCOMPARE(certList.size(), 1); + + const QSslCertificate &cert = certList.at(0); + QVERIFY(!cert.isNull()); + + QMultiMap san = cert.subjectAlternativeNames(); + QVERIFY(!san.isEmpty()); + + QString dnssan = san.value(QSsl::DnsEntry); + QVERIFY(!dnssan.isEmpty()); + QVERIFY(dnssan != "www.bank.com"); + + static const char realSAN[] = "www.bank.com\0www.badguy.com"; + QCOMPARE(dnssan, QString::fromLatin1(realSAN, sizeof realSAN - 1)); +} + +void tst_QSslCertificate::largeSerialNumber() +{ + QList certList = + QSslCertificate::fromPath(SRCDIR "more-certificates/cert-large-serial-number.pem"); + + QCOMPARE(certList.size(), 1); + + const QSslCertificate &cert = certList.at(0); + QVERIFY(!cert.isNull()); + QCOMPARE(cert.serialNumber(), QByteArray("01:02:03:04:05:06:07:08:09:10:aa:bb:cc:dd:ee:ff:17:18:19:20")); +} + +void tst_QSslCertificate::largeExpirationDate() // QTBUG-12489 +{ + QList certList = + QSslCertificate::fromPath(SRCDIR "more-certificates/cert-large-expiration-date.pem"); + + QCOMPARE(certList.size(), 1); + + const QSslCertificate &cert = certList.at(0); + QVERIFY(!cert.isNull()); + QCOMPARE(cert.effectiveDate().toUTC(), QDateTime(QDate(2010, 8, 4), QTime(9, 53, 41), Qt::UTC)); + // if the date is larger than 2049, then the generalized time format is used + QCOMPARE(cert.expiryDate().toUTC(), QDateTime(QDate(2051, 8, 29), QTime(9, 53, 41), Qt::UTC)); +} + +void tst_QSslCertificate::blacklistedCertificates() +{ + QList blacklistedCerts = QSslCertificate::fromPath("more-certificates/blacklisted*.pem", QSsl::Pem, QRegExp::Wildcard); + QVERIFY2(blacklistedCerts.count() > 0, "Please run this test from the source directory"); + for (int a = 0; a < blacklistedCerts.count(); a++) { + QVERIFY(! blacklistedCerts.at(a).isValid()); + } +} + +void tst_QSslCertificate::toText() +{ + QList certList = + QSslCertificate::fromPath(SRCDIR "more-certificates/cert-large-expiration-date.pem"); + + QCOMPARE(certList.size(), 1); + const QSslCertificate &cert = certList.at(0); + + // Openssl's cert dump method changed slightly between 0.9.8 and 1.0.0 versions, so we want it to match any output + + QFile fOld(SRCDIR "more-certificates/cert-large-expiration-date.txt.0.9.8"); + QVERIFY(fOld.open(QIODevice::ReadOnly)); + QByteArray txtOld = fOld.readAll(); + + QFile fNew(SRCDIR "more-certificates/cert-large-expiration-date.txt.1.0.0"); + QVERIFY(fNew.open(QIODevice::ReadOnly)); + QByteArray txtNew = fNew.readAll(); + QVERIFY(txtOld == cert.toText() || txtNew == cert.toText()); +} + +void tst_QSslCertificate::multipleCommonNames() +{ + QList certList = + QSslCertificate::fromPath(SRCDIR "more-certificates/test-cn-two-cns-cert.pem"); + QVERIFY2(certList.count() > 0, "Please run this test from the source directory"); + + QStringList commonNames = certList[0].subjectInfo(QSslCertificate::CommonName); + QVERIFY(commonNames.contains(QString("www.example.com"))); + QVERIFY(commonNames.contains(QString("www2.example.com"))); +} + +void tst_QSslCertificate::subjectAndIssuerAttributes() +{ + QList certList = + QSslCertificate::fromPath(SRCDIR "more-certificates/test-cn-with-drink-cert.pem"); + QVERIFY2(certList.count() > 0, "Please run this test from the source directory"); + + QList attributes = certList[0].subjectInfoAttributes(); + QVERIFY(attributes.contains(QByteArray("favouriteDrink"))); + attributes.clear(); + + certList = QSslCertificate::fromPath(SRCDIR "more-certificates/natwest-banking.pem"); + QVERIFY2(certList.count() > 0, "Please run this test from the source directory"); + + attributes = certList[0].subjectInfoAttributes(); + QVERIFY(attributes.contains(QByteArray("1.3.6.1.4.1.311.60.2.1.3"))); +} + +void tst_QSslCertificate::verify() +{ + QList errors; + QList toVerify; + + // Like QVERIFY, but be verbose about the content of `errors' when failing +#define VERIFY_VERBOSE(A) \ + QVERIFY2((A), \ + qPrintable(QString("errors: %1").arg(toString(errors))) \ + ) + + // Empty chain is unspecified error + errors = QSslCertificate::verify(toVerify); + VERIFY_VERBOSE(errors.count() == 1); + VERIFY_VERBOSE(errors[0] == QSslError(QSslError::UnspecifiedError)); + errors.clear(); + + // Verify a valid cert signed by a CA + QList caCerts = QSslCertificate::fromPath(SRCDIR "verify-certs/cacert.pem"); + QSslSocket::addDefaultCaCertificate(caCerts.first()); + + toVerify = QSslCertificate::fromPath(SRCDIR "verify-certs/test-ocsp-good-cert.pem"); + + errors = QSslCertificate::verify(toVerify); + VERIFY_VERBOSE(errors.count() == 0); + errors.clear(); + + // Test a blacklisted certificate + toVerify = QSslCertificate::fromPath(SRCDIR "verify-certs/test-addons-mozilla-org-cert.pem"); + errors = QSslCertificate::verify(toVerify); + bool foundBlack = false; + foreach (const QSslError &error, errors) { + if (error.error() == QSslError::CertificateBlacklisted) { + foundBlack = true; + break; + } + } + QVERIFY(foundBlack); + errors.clear(); + + // This one is expired and untrusted + toVerify = QSslCertificate::fromPath(SRCDIR "more-certificates/cert-large-serial-number.pem"); + errors = QSslCertificate::verify(toVerify); + VERIFY_VERBOSE(errors.contains(QSslError(QSslError::SelfSignedCertificate, toVerify[0]))); + VERIFY_VERBOSE(errors.contains(QSslError(QSslError::CertificateExpired, toVerify[0]))); + errors.clear(); + toVerify.clear(); + + // This one is signed by a valid cert, but the signer is not a valid CA + toVerify << QSslCertificate::fromPath(SRCDIR "verify-certs/test-intermediate-not-ca-cert.pem").first(); + toVerify << QSslCertificate::fromPath(SRCDIR "verify-certs/test-ocsp-good-cert.pem").first(); + errors = QSslCertificate::verify(toVerify); + VERIFY_VERBOSE(errors.contains(QSslError(QSslError::InvalidCaCertificate, toVerify[1]))); + toVerify.clear(); + + // This one is signed by a valid cert, and the signer is a valid CA + toVerify << QSslCertificate::fromPath(SRCDIR "verify-certs/test-intermediate-is-ca-cert.pem").first(); + toVerify << QSslCertificate::fromPath(SRCDIR "verify-certs/test-intermediate-ca-cert.pem").first(); + errors = QSslCertificate::verify(toVerify); + VERIFY_VERBOSE(errors.count() == 0); + + // Recheck the above with hostname validation + errors = QSslCertificate::verify(toVerify, QLatin1String("example.com")); + VERIFY_VERBOSE(errors.count() == 0); + + // Recheck the above with a bad hostname + errors = QSslCertificate::verify(toVerify, QLatin1String("fail.example.com")); + VERIFY_VERBOSE(errors.contains(QSslError(QSslError::HostNameMismatch, toVerify[0]))); + toVerify.clear(); + +#undef VERIFY_VERBOSE +} + +QString tst_QSslCertificate::toString(const QList& errors) +{ + QStringList errorStrings; + + foreach (const QSslError& error, errors) { + errorStrings.append(QLatin1String("\"") + error.errorString() + QLatin1String("\"")); + } + + return QLatin1String("[ ") + errorStrings.join(QLatin1String(", ")) + QLatin1String(" ]"); +} + +#endif // QT_NO_OPENSSL + +QTEST_MAIN(tst_QSslCertificate) +#include "tst_qsslcertificate.moc" diff --git a/tests/auto/network/ssl/qsslcertificate/verify-certs/README b/tests/auto/network/ssl/qsslcertificate/verify-certs/README new file mode 100644 index 0000000000..87cb293ef6 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/verify-certs/README @@ -0,0 +1,2 @@ +openssl verify -CAfile cacert.pem -untrusted test-intermediate-ca-cert.pem test-intermediate-is-ca-cert.pem +openssl verify -CAfile cacert.pem -untrusted test-ocsp-good-cert.pem test-intermediate-not-ca-cert.pem diff --git a/tests/auto/network/ssl/qsslcertificate/verify-certs/cacert.pem b/tests/auto/network/ssl/qsslcertificate/verify-certs/cacert.pem new file mode 100644 index 0000000000..8c75c54bcb --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/verify-certs/cacert.pem @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID6zCCAtOgAwIBAgIJAP4bjANFSx0BMA0GCSqGSIb3DQEBBQUAMIGrMSYwJAYD +VQQDEx1XZXN0cG9pbnQgQ2VydGlmaWNhdGUgVGVzdCBDQTETMBEGA1UECBMKTGFu +Y2FzaGlyZTELMAkGA1UEBhMCVUsxHTAbBgkqhkiG9w0BCQEWDmNhQGV4YW1wbGUu +Y29tMUAwPgYDVQQKEzdXZXN0cG9pbnQgQ2VydGlmaWNhdGUgVGVzdCBSb290IENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTExMDczMTIxMDExNloXDTIxMDcyODIx +MDExNlowgasxJjAkBgNVBAMTHVdlc3Rwb2ludCBDZXJ0aWZpY2F0ZSBUZXN0IENB +MRMwEQYDVQQIEwpMYW5jYXNoaXJlMQswCQYDVQQGEwJVSzEdMBsGCSqGSIb3DQEJ +ARYOY2FAZXhhbXBsZS5jb20xQDA+BgNVBAoTN1dlc3Rwb2ludCBDZXJ0aWZpY2F0 +ZSBUZXN0IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQC5xMKXviXuxFO67WzFIImO5RY3Y+dqt7maTB+p +JiHkn98rJoBB4J1cDnEUIs5ErO+kqOjW7JwF50fePNJ5K+I6SbRVn9gxAI59ZA6O +9UvOPZOw4/6GM24UY4B4mUcp8oXg9fhwgtjVhfXiMD2GvKQq3RazIiCoSW4aJWEq +L58Q+sIo+jL72qwk648xIwIhuC3XzcOOE/+rCOtZmu812/NN08UfsL2qup0aaaGv +aL36n6OIx5AYFcCD5uOxXAmUy14mhwQyDHAl6K42ghSm5b43VMMSQ+N9AQpentWl +RH6Vt1eY52YTxjNxpRlj88GBnYxdr8WgjKOV7v8OPGXP6zWlAgMBAAGjEDAOMAwG +A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADptDEfvsh8aq/tTc7ciGkHG +jh7WFELVTcdWBTyveZ24298Hl9UOfsAfLqjMGMs3delAaZocchba9Og2xSZyRstH +GUtlJXd4PnSJSx/TksPf2DCANo5sxBWBITs1Iprm3Nlm3/xPZM2QLIamRYi2J6Ed +JTfWvMpoaW1umJX49jKqk1gfdcS6eUSaXetgYP2FQV7DstqPLYfQ731nEXZ1LXFM +PO7IoPccqk4YJ0KOV7hFb7NCq4a6cz/Gf0S0qJ44vqHz6iRZpmWIo5UFivwtLw9r +iMbdJ1mCCMR0oN5om3muKc7Sz+l2ItxdYMcLkZ1/3ouvQqOX+qIOrYEUN1RZCzI= +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslcertificate/verify-certs/test-addons-mozilla-org-cert.pem b/tests/auto/network/ssl/qsslcertificate/verify-certs/test-addons-mozilla-org-cert.pem new file mode 100644 index 0000000000..07123e8577 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/verify-certs/test-addons-mozilla-org-cert.pem @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +MIIF+DCCBOCgAwIBAgIRAJI51TSPQNFpWnRUcOHyP0MwDQYJKoZIhvcNAQEFBQAw +gZcxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJVVDEXMBUGA1UEBxMOU2FsdCBMYWtl +IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEhMB8GA1UECxMY +aHR0cDovL3d3dy51c2VydHJ1c3QuY29tMR8wHQYDVQQDExZVVE4tVVNFUkZpcnN0 +LUhhcmR3YXJlMB4XDTExMDMxNTAwMDAwMFoXDTE0MDMxNDIzNTk1OVowgeIxCzAJ +BgNVBAYTAlVTMQ4wDAYDVQQREwUzODQ3NzEQMA4GA1UECBMHRmxvcmlkYTEQMA4G +A1UEBxMHRW5nbGlzaDEXMBUGA1UECRMOU2VhIFZpbGxhZ2UgMTAxFDASBgNVBAoT +C0dvb2dsZSBMdGQuMRMwEQYDVQQLEwpUZWNoIERlcHQuMSgwJgYDVQQLEx9Ib3N0 +ZWQgYnkgR1RJIEdyb3VwIENvcnBvcmF0aW9uMRQwEgYDVQQLEwtQbGF0aW51bVNT +TDEbMBkGA1UEAxMSYWRkb25zLm1vemlsbGEub3JnMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAq8ZtNvMVc3iDc850hdWu7LLw4CQfE4O4IKy7mv6Iu6uh +HQsfRQCqSbc1Nwxq70dMudG+41cSBI2Sx7bsAby22seBOCCtcoXmDvyBbAetaHY4 +xUTXzMZKxZc+ZPRR5vB+suxW9yWCTUmYyxaY3SPxiZHRF5dAmSbW4qIrXt+9ifIb +GlMtzFBBetA9KgxVcBQB6VhJEHoLk4KL4R7tOoAQgs6WijTwzNfTubRQh1VUCbid +QihVAOWMNVS/3SWRRrcN5V2DqOWL+4TkPK522sRDK1t0C/i+XWjxeFu1zn3xXZlA +2sruOIFQvpihbLgkrfOvjA/XESgshBhMfbXZjzC1GwIDAQABo4IB8DCCAewwHwYD +VR0jBBgwFoAUoXJfJhsomEOVXQc31YWWnUvSw0UwHQYDVR0OBBYEFN2A0lQ990xw +yqOw3TR6MuToO1o7MA4GA1UdDwEB/wQEAwIFoDAMBgNVHRMBAf8EAjAAMB0GA1Ud +JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBGBgNVHSAEPzA9MDsGDCsGAQQBsjEB +AgEDBDArMCkGCCsGAQUFBwIBFh1odHRwczovL3NlY3VyZS5jb21vZG8uY29tL0NQ +UzB7BgNVHR8EdDByMDigNqA0hjJodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9VVE4t +VVNFUkZpcnN0LUhhcmR3YXJlLmNybDA2oDSgMoYwaHR0cDovL2NybC5jb21vZG8u +bmV0L1VUTi1VU0VSRmlyc3QtSGFyZHdhcmUuY3JsMHEGCCsGAQUFBwEBBGUwYzA7 +BggrBgEFBQcwAoYvaHR0cDovL2NydC5jb21vZG9jYS5jb20vVVROQWRkVHJ1c3RT +ZXJ2ZXJDQS5jcnQwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmNvbW9kb2NhLmNv +bTA1BgNVHREELjAsghJhZGRvbnMubW96aWxsYS5vcmeCFnd3dy5hZGRvbnMubW96 +aWxsYS5vcmcwDQYJKoZIhvcNAQEFBQADggEBADM7YxX8sewULJPddZTegVrZTpm+ ++0qkOVVNoUB63hMqh6k3z+jV+63Re21vjCCHglTmV0m8ICiEzdYB2ZOLF24jZuWE +yIA/xqFwgOTsTR35/JFac2IpmvcgHGHgizmfyrx+jd282bHjn57fFVORIVIL2Roj +D2Y226yTlkqjpSLPKfeimaj2ttlArtl+tvZYLpusNspkj2VS3IacgqtuUEvaX/oF +AIgwDt6NVr+BR409BuKyYpJnj57ImrLlBrhwJLh3fCMKOMN5CNixUZ2slRHHQBee +oxyP8hGnaCfaSQWEGHxYLQFnXOWfoSm7SjlFL78Rqnmi7bTUtWVDt5NGitM= +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslcertificate/verify-certs/test-intermediate-ca-cert.pem b/tests/auto/network/ssl/qsslcertificate/verify-certs/test-intermediate-ca-cert.pem new file mode 100644 index 0000000000..d00490caba --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/verify-certs/test-intermediate-ca-cert.pem @@ -0,0 +1,66 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 28 (0x1c) + Signature Algorithm: sha1WithRSAEncryption + Issuer: CN=Westpoint Certificate Test CA, ST=Lancashire, C=UK/emailAddress=ca@example.com, O=Westpoint Certificate Test Root Certification Authority + Validity + Not Before: Jul 31 21:01:18 2011 GMT + Not After : Jul 28 21:01:18 2021 GMT + Subject: ST=Lancashire, C=UK/emailAddress=test@example.com, O=Test intermediate CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1024 bit) + Modulus: + 00:bc:bd:83:c1:bc:36:d8:9c:74:68:5a:46:48:25: + 83:59:f8:35:1e:8f:dc:2c:52:3b:7c:2e:ea:40:c4: + 93:b6:39:31:df:f5:a6:f8:01:17:67:93:21:59:9b: + 89:7f:ed:2a:19:7b:25:a5:e1:71:12:99:e5:14:28: + df:75:b5:17:1c:3b:1d:3d:74:48:4f:b7:42:f4:3a: + ab:56:05:2b:fc:d3:27:97:01:08:5b:ad:26:9b:f2: + 87:51:9c:7e:e1:f1:ef:1c:bf:ad:7e:38:d9:76:89: + 30:a6:8c:2f:6f:87:9f:9e:57:13:14:b4:45:30:f3: + be:58:df:8a:d2:ee:7b:1d:89 + Exponent: 65537 (0x10001) + X509v3 extensions: + Authority Information Access: + OCSP - URI:http://ocsp.example.com:8888/ + + X509v3 Basic Constraints: + CA:TRUE + Signature Algorithm: sha1WithRSAEncryption + 33:84:9d:0e:b2:59:04:dc:ef:e3:04:8b:00:6c:64:ea:58:9e: + 36:59:76:27:59:a0:b8:ee:0d:86:83:ff:db:65:eb:6c:1f:16: + 47:e7:f5:e6:c3:88:81:73:7e:ed:12:8d:7e:fd:5e:b1:5c:68: + 47:f8:f9:ca:e3:e0:c0:f3:12:b2:24:3b:77:2c:98:de:05:6d: + a8:ec:27:b8:af:ab:84:25:26:73:b4:58:4c:7c:c1:74:97:98: + ab:0e:e6:99:70:bc:38:b0:9a:e3:d9:5c:75:fa:46:d2:87:55: + 09:86:8f:ef:4a:e4:ef:3e:32:c6:ac:9d:27:86:29:b8:78:38: + 7b:87:6c:57:72:bd:57:99:73:36:db:fa:52:bd:7b:a7:05:cd: + 28:b8:85:fc:11:47:5e:c6:77:72:6a:fb:73:3e:8b:a4:6d:f8: + 17:f4:12:d5:36:e0:ef:5c:f8:b2:a1:69:3e:4c:cf:86:5f:63: + f6:02:60:95:7f:61:e8:cb:7f:14:66:da:36:2e:78:13:3e:68: + ae:3f:13:c1:79:88:18:18:3f:23:f3:9a:e1:e7:7e:ae:50:e4: + b7:80:76:31:92:74:79:2c:de:d0:74:fe:81:7c:f6:01:14:6a: + 1f:5f:88:85:6a:11:1d:50:af:f1:97:4d:67:40:c3:e9:ae:6f: + 60:e2:bc:e2 +-----BEGIN CERTIFICATE----- +MIIDUDCCAjigAwIBAgIBHDANBgkqhkiG9w0BAQUFADCBqzEmMCQGA1UEAxMdV2Vz +dHBvaW50IENlcnRpZmljYXRlIFRlc3QgQ0ExEzARBgNVBAgTCkxhbmNhc2hpcmUx +CzAJBgNVBAYTAlVLMR0wGwYJKoZIhvcNAQkBFg5jYUBleGFtcGxlLmNvbTFAMD4G +A1UEChM3V2VzdHBvaW50IENlcnRpZmljYXRlIFRlc3QgUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eTAeFw0xMTA3MzEyMTAxMThaFw0yMTA3MjgyMTAxMThaMGIx +EzARBgNVBAgTCkxhbmNhc2hpcmUxCzAJBgNVBAYTAlVLMR8wHQYJKoZIhvcNAQkB +FhB0ZXN0QGV4YW1wbGUuY29tMR0wGwYDVQQKExRUZXN0IGludGVybWVkaWF0ZSBD +QTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvL2Dwbw22Jx0aFpGSCWDWfg1 +Ho/cLFI7fC7qQMSTtjkx3/Wm+AEXZ5MhWZuJf+0qGXslpeFxEpnlFCjfdbUXHDsd +PXRIT7dC9DqrVgUr/NMnlwEIW60mm/KHUZx+4fHvHL+tfjjZdokwpowvb4efnlcT +FLRFMPO+WN+K0u57HYkCAwEAAaNLMEkwOQYIKwYBBQUHAQEELTArMCkGCCsGAQUF +BzABhh1odHRwOi8vb2NzcC5leGFtcGxlLmNvbTo4ODg4LzAMBgNVHRMEBTADAQH/ +MA0GCSqGSIb3DQEBBQUAA4IBAQAzhJ0OslkE3O/jBIsAbGTqWJ42WXYnWaC47g2G +g//bZetsHxZH5/Xmw4iBc37tEo1+/V6xXGhH+PnK4+DA8xKyJDt3LJjeBW2o7Ce4 +r6uEJSZztFhMfMF0l5irDuaZcLw4sJrj2Vx1+kbSh1UJho/vSuTvPjLGrJ0nhim4 +eDh7h2xXcr1XmXM22/pSvXunBc0ouIX8EUdexndyavtzPoukbfgX9BLVNuDvXPiy +oWk+TM+GX2P2AmCVf2Hoy38UZto2LngTPmiuPxPBeYgYGD8j85rh536uUOS3gHYx +knR5LN7QdP6BfPYBFGofX4iFahEdUK/xl01nQMPprm9g4rzi +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslcertificate/verify-certs/test-intermediate-is-ca-cert.pem b/tests/auto/network/ssl/qsslcertificate/verify-certs/test-intermediate-is-ca-cert.pem new file mode 100644 index 0000000000..396cad86cb --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/verify-certs/test-intermediate-is-ca-cert.pem @@ -0,0 +1,53 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 29 (0x1d) + Signature Algorithm: sha1WithRSAEncryption + Issuer: ST=Lancashire, C=UK/emailAddress=test@example.com, O=Test intermediate CA + Validity + Not Before: Jul 31 21:01:18 2011 GMT + Not After : Jul 28 21:01:18 2021 GMT + Subject: CN=example.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1024 bit) + Modulus: + 00:c9:bb:98:5b:27:cd:b1:8a:a9:38:fc:aa:bb:ad: + a1:ed:cb:94:94:3e:79:90:ae:35:f3:87:b1:2a:4e: + d5:ff:55:93:e0:1a:68:2a:36:94:05:38:a7:72:64: + a3:31:0f:61:5c:ec:76:41:f1:35:4a:5e:bc:ef:51: + 90:9e:33:b4:08:7a:3f:f0:04:a8:46:99:96:25:b3: + 03:c8:cd:8c:33:42:76:82:b9:db:61:c6:91:ed:76: + 86:ae:04:38:d7:e5:5c:a9:a9:f9:b6:13:f4:90:40: + 6d:ec:2f:ba:ed:bc:ff:88:05:f0:7b:c8:ac:bd:d0: + 72:3a:91:64:86:06:89:66:0d + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Authority Information Access: + OCSP - URI:http://ocsp.example.com:8888/ + + Signature Algorithm: sha1WithRSAEncryption + 22:30:97:01:ea:d0:a8:d8:b5:32:97:c8:c9:8b:7d:01:02:53: + 74:f8:0a:10:dc:fc:73:b2:50:bb:59:47:f3:e4:9f:44:94:d5: + ca:c0:64:da:83:00:95:43:15:a5:e3:30:ce:66:ca:55:8c:16: + 03:1e:55:02:8b:c7:ad:ed:2e:ae:ee:31:59:53:37:ff:26:86: + 93:9d:e2:69:2e:c0:2a:66:38:a5:b5:54:a1:02:0a:83:67:e0: + 91:cf:fc:09:c3:70:71:b6:cf:fc:d3:e9:9f:f5:1c:4d:55:ec: + 66:f7:07:71:fc:d6:17:de:e1:ab:e6:f2:7b:83:46:1e:b9:96: + 95:8f +-----BEGIN CERTIFICATE----- +MIICNjCCAZ+gAwIBAgIBHTANBgkqhkiG9w0BAQUFADBiMRMwEQYDVQQIEwpMYW5j +YXNoaXJlMQswCQYDVQQGEwJVSzEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxl +LmNvbTEdMBsGA1UEChMUVGVzdCBpbnRlcm1lZGlhdGUgQ0EwHhcNMTEwNzMxMjEw +MTE4WhcNMjEwNzI4MjEwMTE4WjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCBnzAN +BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAybuYWyfNsYqpOPyqu62h7cuUlD55kK41 +84exKk7V/1WT4BpoKjaUBTincmSjMQ9hXOx2QfE1Sl6871GQnjO0CHo/8ASoRpmW +JbMDyM2MM0J2grnbYcaR7XaGrgQ41+Vcqan5thP0kEBt7C+67bz/iAXwe8isvdBy +OpFkhgaJZg0CAwEAAaNIMEYwCQYDVR0TBAIwADA5BggrBgEFBQcBAQQtMCswKQYI +KwYBBQUHMAGGHWh0dHA6Ly9vY3NwLmV4YW1wbGUuY29tOjg4ODgvMA0GCSqGSIb3 +DQEBBQUAA4GBACIwlwHq0KjYtTKXyMmLfQECU3T4ChDc/HOyULtZR/Pkn0SU1crA +ZNqDAJVDFaXjMM5mylWMFgMeVQKLx63tLq7uMVlTN/8mhpOd4mkuwCpmOKW1VKEC +CoNn4JHP/AnDcHG2z/zT6Z/1HE1V7Gb3B3H81hfe4avm8nuDRh65lpWP +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslcertificate/verify-certs/test-intermediate-not-ca-cert.pem b/tests/auto/network/ssl/qsslcertificate/verify-certs/test-intermediate-not-ca-cert.pem new file mode 100644 index 0000000000..34ad2b10a8 --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/verify-certs/test-intermediate-not-ca-cert.pem @@ -0,0 +1,54 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 27 (0x1b) + Signature Algorithm: sha1WithRSAEncryption + Issuer: CN=example.com, ST=Lancashire, C=UK/emailAddress=test@example.com, O=Some organisation + Validity + Not Before: Jul 31 21:01:18 2011 GMT + Not After : Jul 28 21:01:18 2021 GMT + Subject: CN=example.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1024 bit) + Modulus: + 00:ea:d6:97:b5:3c:f4:37:8a:58:b4:7a:49:31:55: + dd:c8:84:ee:36:f6:72:3a:31:99:d1:df:af:bb:f9: + 17:e9:d8:47:d2:20:4b:94:ce:ea:c1:6b:23:9a:da: + 02:41:29:51:34:05:13:c0:98:4d:87:f8:91:a8:85: + 81:e4:ab:26:3d:26:59:29:16:7d:04:db:57:7b:f0: + b6:2b:5d:cf:e7:82:ba:83:a7:bc:63:43:03:2a:2b: + 18:40:89:4c:1e:90:bc:bf:10:24:81:50:0d:2e:e8: + 8e:a9:0a:fc:f8:cd:97:98:3c:cc:55:b7:f2:b2:0d: + 0e:36:53:3a:b2:d0:45:90:8b + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Authority Information Access: + OCSP - URI:http://ocsp.example.com:8888/ + + Signature Algorithm: sha1WithRSAEncryption + 82:d8:53:9c:d8:0b:0a:b3:9d:b4:0a:9f:93:ec:96:a6:31:6b: + 79:c9:d2:1c:76:0b:b7:f3:9f:b9:7a:dd:d7:b7:7b:26:ba:0a: + 54:2a:a3:ad:89:8e:3c:b8:8e:ea:09:53:58:73:9a:b3:a0:40: + 90:02:f2:60:04:b8:f0:2a:61:bd:91:9b:5e:81:5f:bf:cc:f2: + 33:33:8a:70:07:f5:ea:c0:05:38:34:f7:dc:ea:0c:74:01:5d: + dd:92:ab:f2:87:64:1b:7c:be:ae:37:c1:6c:ae:99:73:a5:aa: + 45:20:32:57:19:cb:30:45:61:2c:3b:23:52:ee:f0:cc:12:80: + 97:34 +-----BEGIN CERTIFICATE----- +MIICSTCCAbKgAwIBAgIBGzANBgkqhkiG9w0BAQUFADB1MRQwEgYDVQQDEwtleGFt +cGxlLmNvbTETMBEGA1UECBMKTGFuY2FzaGlyZTELMAkGA1UEBhMCVUsxHzAdBgkq +hkiG9w0BCQEWEHRlc3RAZXhhbXBsZS5jb20xGjAYBgNVBAoTEVNvbWUgb3JnYW5p +c2F0aW9uMB4XDTExMDczMTIxMDExOFoXDTIxMDcyODIxMDExOFowFjEUMBIGA1UE +AxMLZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOrWl7U8 +9DeKWLR6STFV3ciE7jb2cjoxmdHfr7v5F+nYR9IgS5TO6sFrI5raAkEpUTQFE8CY +TYf4kaiFgeSrJj0mWSkWfQTbV3vwtitdz+eCuoOnvGNDAyorGECJTB6QvL8QJIFQ +DS7ojqkK/PjNl5g8zFW38rINDjZTOrLQRZCLAgMBAAGjSDBGMAkGA1UdEwQCMAAw +OQYIKwYBBQUHAQEELTArMCkGCCsGAQUFBzABhh1odHRwOi8vb2NzcC5leGFtcGxl +LmNvbTo4ODg4LzANBgkqhkiG9w0BAQUFAAOBgQCC2FOc2AsKs520Cp+T7JamMWt5 +ydIcdgu385+5et3Xt3smugpUKqOtiY48uI7qCVNYc5qzoECQAvJgBLjwKmG9kZte +gV+/zPIzM4pwB/XqwAU4NPfc6gx0AV3dkqvyh2QbfL6uN8FsrplzpapFIDJXGcsw +RWEsOyNS7vDMEoCXNA== +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslcertificate/verify-certs/test-ocsp-good-cert.pem b/tests/auto/network/ssl/qsslcertificate/verify-certs/test-ocsp-good-cert.pem new file mode 100644 index 0000000000..34b26c6d5e --- /dev/null +++ b/tests/auto/network/ssl/qsslcertificate/verify-certs/test-ocsp-good-cert.pem @@ -0,0 +1,67 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1 (0x1) + Signature Algorithm: sha1WithRSAEncryption + Issuer: CN=Westpoint Certificate Test CA, ST=Lancashire, C=UK/emailAddress=ca@example.com, O=Westpoint Certificate Test Root Certification Authority + Validity + Not Before: Jul 31 21:01:16 2011 GMT + Not After : Jul 28 21:01:16 2021 GMT + Subject: CN=example.com, ST=Lancashire, C=UK/emailAddress=test@example.com, O=Some organisation + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1024 bit) + Modulus: + 00:97:c9:92:27:81:a7:4c:64:82:a2:30:d6:07:b7: + 57:e0:9c:ea:cd:eb:53:be:ea:b6:b5:47:66:d0:68: + 54:25:a7:ed:21:5c:dc:fd:da:41:f6:c7:c0:35:ae: + 97:72:fd:8b:af:29:3d:38:5a:67:8b:39:8a:ce:86: + 25:0f:38:a7:b5:38:b3:8e:81:f0:ea:79:99:cb:f5: + 23:64:55:f3:4b:a4:b6:23:64:29:ea:ba:f3:29:52: + a7:7f:32:dc:0d:b6:d9:d4:e6:13:de:01:41:86:9a: + 2d:8f:bb:0c:18:88:09:ac:d4:6a:e9:cb:8a:17:8a: + 85:09:a6:ae:a6:1c:05:e9:55 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Authority Information Access: + OCSP - URI:http://ocsp.example.com:8888/ + + Signature Algorithm: sha1WithRSAEncryption + 8b:9b:96:fb:8e:1b:77:f5:70:39:fe:76:51:ac:a9:6b:80:a5: + b7:95:8b:c3:1a:9c:1f:bb:d1:d1:68:43:40:96:62:d6:a6:da: + d9:fd:9d:9a:9e:8a:84:fa:f5:54:ce:a8:d7:37:c7:0c:95:fc: + 11:8b:e9:32:53:e5:59:61:0a:53:70:f3:d6:ed:3f:b1:f4:49: + bf:86:c1:77:0d:b1:ac:65:7e:62:d2:f2:5a:31:50:a7:ed:28: + bb:63:d5:f3:4f:43:3a:3f:bf:3b:d0:94:aa:a1:74:95:be:a4: + 0f:8b:e0:6f:d8:33:84:76:71:b2:da:f4:0e:1e:d2:eb:f0:c3: + 1e:33:79:21:35:93:18:05:38:db:63:85:1a:e4:84:41:0a:c3: + fb:fd:5c:69:3d:18:0a:38:b8:16:18:d3:23:b9:51:47:2e:54: + 08:d1:fc:2e:b6:63:62:78:9c:26:59:c2:5e:5a:38:76:47:e7: + f0:f8:7b:b7:00:46:34:b0:44:28:a9:33:d7:e5:1d:52:c8:fb: + 32:a5:25:86:21:0c:80:f0:4b:37:60:a0:45:69:9f:6b:b0:34: + 91:5e:4c:62:45:99:83:1d:80:48:78:bb:ee:d4:83:39:76:c3: + e6:fb:31:e9:20:f0:64:90:24:4e:c6:07:75:40:1f:7e:97:77: + 1f:bf:a2:ef +-----BEGIN CERTIFICATE----- +MIIDYDCCAkigAwIBAgIBATANBgkqhkiG9w0BAQUFADCBqzEmMCQGA1UEAxMdV2Vz +dHBvaW50IENlcnRpZmljYXRlIFRlc3QgQ0ExEzARBgNVBAgTCkxhbmNhc2hpcmUx +CzAJBgNVBAYTAlVLMR0wGwYJKoZIhvcNAQkBFg5jYUBleGFtcGxlLmNvbTFAMD4G +A1UEChM3V2VzdHBvaW50IENlcnRpZmljYXRlIFRlc3QgUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eTAeFw0xMTA3MzEyMTAxMTZaFw0yMTA3MjgyMTAxMTZaMHUx +FDASBgNVBAMTC2V4YW1wbGUuY29tMRMwEQYDVQQIEwpMYW5jYXNoaXJlMQswCQYD +VQQGEwJVSzEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbTEaMBgGA1UE +ChMRU29tZSBvcmdhbmlzYXRpb24wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGB +AJfJkieBp0xkgqIw1ge3V+Cc6s3rU77qtrVHZtBoVCWn7SFc3P3aQfbHwDWul3L9 +i68pPThaZ4s5is6GJQ84p7U4s46B8Op5mcv1I2RV80uktiNkKeq68ylSp38y3A22 +2dTmE94BQYaaLY+7DBiICazUaunLiheKhQmmrqYcBelVAgMBAAGjSDBGMAkGA1Ud +EwQCMAAwOQYIKwYBBQUHAQEELTArMCkGCCsGAQUFBzABhh1odHRwOi8vb2NzcC5l +eGFtcGxlLmNvbTo4ODg4LzANBgkqhkiG9w0BAQUFAAOCAQEAi5uW+44bd/VwOf52 +Uaypa4Clt5WLwxqcH7vR0WhDQJZi1qba2f2dmp6KhPr1VM6o1zfHDJX8EYvpMlPl +WWEKU3Dz1u0/sfRJv4bBdw2xrGV+YtLyWjFQp+0ou2PV809DOj+/O9CUqqF0lb6k +D4vgb9gzhHZxstr0Dh7S6/DDHjN5ITWTGAU422OFGuSEQQrD+/1caT0YCji4FhjT +I7lRRy5UCNH8LrZjYnicJlnCXlo4dkfn8Ph7twBGNLBEKKkz1+UdUsj7MqUlhiEM +gPBLN2CgRWmfa7A0kV5MYkWZgx2ASHi77tSDOXbD5vsx6SDwZJAkTsYHdUAffpd3 +H7+i7w== +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslcipher/.gitignore b/tests/auto/network/ssl/qsslcipher/.gitignore new file mode 100644 index 0000000000..13137ce3aa --- /dev/null +++ b/tests/auto/network/ssl/qsslcipher/.gitignore @@ -0,0 +1 @@ +tst_qsslcipher diff --git a/tests/auto/network/ssl/qsslcipher/qsslcipher.pro b/tests/auto/network/ssl/qsslcipher/qsslcipher.pro new file mode 100644 index 0000000000..78fd387069 --- /dev/null +++ b/tests/auto/network/ssl/qsslcipher/qsslcipher.pro @@ -0,0 +1,18 @@ +load(qttest_p4) + +SOURCES += tst_qsslcipher.cpp +!wince*:win32:LIBS += -lws2_32 +QT = core network + +TARGET = tst_qsslcipher + +win32 { + CONFIG(debug, debug|release) { + DESTDIR = debug +} else { + DESTDIR = release + } +} + +symbian: TARGET.CAPABILITY = NetworkServices + diff --git a/tests/auto/network/ssl/qsslcipher/tst_qsslcipher.cpp b/tests/auto/network/ssl/qsslcipher/tst_qsslcipher.cpp new file mode 100644 index 0000000000..5fe4d03bdd --- /dev/null +++ b/tests/auto/network/ssl/qsslcipher/tst_qsslcipher.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include + +#include +#include + +class tst_QSslCipher : public QObject +{ + Q_OBJECT + +public: + tst_QSslCipher(); + virtual ~tst_QSslCipher(); + +public slots: + void initTestCase_data(); + void init(); + void cleanup(); + +#ifndef QT_NO_OPENSSL + +private slots: + void constructing(); + +#endif +}; + +tst_QSslCipher::tst_QSslCipher() +{ +} + +tst_QSslCipher::~tst_QSslCipher() +{ + +} + +void tst_QSslCipher::initTestCase_data() +{ +} + +void tst_QSslCipher::init() +{ +} + +void tst_QSslCipher::cleanup() +{ +} + +#ifndef QT_NO_OPENSSL + +void tst_QSslCipher::constructing() +{ + QSslCipher cipher; +} + +#endif // QT_NO_OPENSSL + +QTEST_MAIN(tst_QSslCipher) +#include "tst_qsslcipher.moc" diff --git a/tests/auto/network/ssl/qsslerror/.gitignore b/tests/auto/network/ssl/qsslerror/.gitignore new file mode 100644 index 0000000000..e30995e94a --- /dev/null +++ b/tests/auto/network/ssl/qsslerror/.gitignore @@ -0,0 +1 @@ +tst_qsslerror diff --git a/tests/auto/network/ssl/qsslerror/qsslerror.pro b/tests/auto/network/ssl/qsslerror/qsslerror.pro new file mode 100644 index 0000000000..5b907fb600 --- /dev/null +++ b/tests/auto/network/ssl/qsslerror/qsslerror.pro @@ -0,0 +1,18 @@ +load(qttest_p4) + +SOURCES += tst_qsslerror.cpp +!wince*:win32:LIBS += -lws2_32 +QT = core network + +TARGET = tst_qsslerror + +win32 { + CONFIG(debug, debug|release) { + DESTDIR = debug +} else { + DESTDIR = release + } +} + +symbian: TARGET.CAPABILITY = NetworkServices + diff --git a/tests/auto/network/ssl/qsslerror/tst_qsslerror.cpp b/tests/auto/network/ssl/qsslerror/tst_qsslerror.cpp new file mode 100644 index 0000000000..3fae88ec00 --- /dev/null +++ b/tests/auto/network/ssl/qsslerror/tst_qsslerror.cpp @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include + +#include +#include + +class tst_QSslError : public QObject +{ + Q_OBJECT + +public: + tst_QSslError(); + virtual ~tst_QSslError(); + + static void enterLoop(int secs) + { + ++loopLevel; + QTestEventLoop::instance().enterLoop(secs); + --loopLevel; + } + static void exitLoop() + { + // Safe exit - if we aren't in an event loop, don't + // exit one. + if (loopLevel > 0) + QTestEventLoop::instance().exitLoop(); + } + static bool timeout() + { + return QTestEventLoop::instance().timeout(); + } + +public slots: + void initTestCase_data(); + void init(); + void cleanup(); + +#ifndef QT_NO_OPENSSL +private slots: + void constructing(); +#endif + +private: + static int loopLevel; +}; + +int tst_QSslError::loopLevel = 0; + +tst_QSslError::tst_QSslError() +{ +} + +tst_QSslError::~tst_QSslError() +{ + +} + +void tst_QSslError::initTestCase_data() +{ +} + +void tst_QSslError::init() +{ +} + +void tst_QSslError::cleanup() +{ +} + +#ifndef QT_NO_OPENSSL + +void tst_QSslError::constructing() +{ + QSslError error; +} + +#endif // QT_NO_OPENSSL + +QTEST_MAIN(tst_QSslError) +#include "tst_qsslerror.moc" diff --git a/tests/auto/network/ssl/qsslkey/.gitignore b/tests/auto/network/ssl/qsslkey/.gitignore new file mode 100644 index 0000000000..7c9a790dd7 --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/.gitignore @@ -0,0 +1 @@ +tst_qsslkey diff --git a/tests/auto/network/ssl/qsslkey/keys/dsa-pri-1024.der b/tests/auto/network/ssl/qsslkey/keys/dsa-pri-1024.der new file mode 100644 index 0000000000..5b4026bef3 Binary files /dev/null and b/tests/auto/network/ssl/qsslkey/keys/dsa-pri-1024.der differ diff --git a/tests/auto/network/ssl/qsslkey/keys/dsa-pri-1024.pem b/tests/auto/network/ssl/qsslkey/keys/dsa-pri-1024.pem new file mode 100644 index 0000000000..cdb16c3841 --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/keys/dsa-pri-1024.pem @@ -0,0 +1,12 @@ +-----BEGIN DSA PRIVATE KEY----- +MIIBuwIBAAKBgQDBDNxGlo12UTCZ4S5XxcfQ8pKaYMaPYCpcJqbRMQ89VshZPFus +ZycnITHQ3fjhHyxPfXFwzKFh9cxvJTGyWmazQBj4YOG09DzPs3fvRiIz5zu1bdJ0 +rH0VJNKdbDmdDH2BKssPrqIjQoLNwjQLcE5Ddg2as8dfg6pPi7QVq2+U6wIVAMMh +a50lRJAJckZuztWEkV4I7H03AoGAfRXSIMUWvfnwLIDuuThj5KhFKML0kd0BxrT9 +H0dICi4pTv7DyH7OWhrCS/025aK/AehrfU+QSiceUPxZLSxeyisSgtxWIolp8zgj +1hBPUW/lpMwNgChoKf8EgagHHY4Y0+eLnL7D/5M4hBX9eQLhK3tvaULbkW4pjR+r +nYW9UEMCgYAhRYQ296sCvQlqjhTyj8/VFahXvXnV2YuQ/2QFm0KHXbRXsmR2ectw +NcaBvD87EZ3KRXQiSXzea6WycOw9YXcTmgzLUefHaBkQ38973pmPIrO91HDJqVE0 +GLOIIbh796Y/9z7//BO4yAXQO9jcxeFKwiB2zYluZOVDOiFFgqqbMgIVAK0dcUpb +9EOsRsRdOVI3hIxssc71 +-----END DSA PRIVATE KEY----- diff --git a/tests/auto/network/ssl/qsslkey/keys/dsa-pri-512.der b/tests/auto/network/ssl/qsslkey/keys/dsa-pri-512.der new file mode 100644 index 0000000000..0fc977f4e4 Binary files /dev/null and b/tests/auto/network/ssl/qsslkey/keys/dsa-pri-512.der differ diff --git a/tests/auto/network/ssl/qsslkey/keys/dsa-pri-512.pem b/tests/auto/network/ssl/qsslkey/keys/dsa-pri-512.pem new file mode 100644 index 0000000000..35210e4dec --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/keys/dsa-pri-512.pem @@ -0,0 +1,8 @@ +-----BEGIN DSA PRIVATE KEY----- +MIH4AgEAAkEA2tY9+lNVCnDx//ciofnvm/OPWi7oMQ6XFFfXrpjltIG+9eRQv0j4 +SqcwHpsqcaC4vjsbHFTDR0p8UyUAbrXzLwIVAOKRP6SgXHveEJ//aztaVltEWqBB +AkAj1tdgG3FQBxG5UJQGl53XsOOBJ9xz21RrrrlkqCO9G9ClNjmQ1XzrkBHije1M +Pb6iOLH1aPYiY6svkxaDMb88AkEAzobcabWo6sag70paK/ihZlSbd4EC5Hl1XjtC +5sKFBkjLK3A79f9TobAwDiHzSxdiLNT0xKd+iOKoWZnAj+TJ2AIUI7+wILGkcyW7 +nb4wf7r1HYIo8bk= +-----END DSA PRIVATE KEY----- diff --git a/tests/auto/network/ssl/qsslkey/keys/dsa-pri-576.der b/tests/auto/network/ssl/qsslkey/keys/dsa-pri-576.der new file mode 100644 index 0000000000..30cea46c1e Binary files /dev/null and b/tests/auto/network/ssl/qsslkey/keys/dsa-pri-576.der differ diff --git a/tests/auto/network/ssl/qsslkey/keys/dsa-pri-576.pem b/tests/auto/network/ssl/qsslkey/keys/dsa-pri-576.pem new file mode 100644 index 0000000000..a398ddcf13 --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/keys/dsa-pri-576.pem @@ -0,0 +1,8 @@ +-----BEGIN DSA PRIVATE KEY----- +MIIBDwIBAAJJANeKU0IdVgDc/dVL6vrEjgZW8lMLrUyjYDhu65sl8LaOmamwvozx +ISFjYaeIMPCloHnn2jijsjQpjnlj5rVRT1MYWt2i56jV+wIVANvg9uDO7mNdcuOW +uT+xveysakC9Akg2q1Vi56KzKVR4T2KIS4yTH3bz2wwiWd80p+SylyOkM/fSIrxJ +IRYGugR1WY7lgWAI8E0mJDXKmajpGSvysDHnjdlSpSVSUNICSD9wBxobS/ySpKs+ +ks11XCPyZiMsf+LjUtT7nK5RBTG0PDEw+82MchouPc4YUPGR+Qf64GB7AxBYQpt/ +YhLXtFVCocZUt0HJBwIUHhfcQUujc40YUkOYavxrU+1eQUY= +-----END DSA PRIVATE KEY----- diff --git a/tests/auto/network/ssl/qsslkey/keys/dsa-pri-960.der b/tests/auto/network/ssl/qsslkey/keys/dsa-pri-960.der new file mode 100644 index 0000000000..d8291b87ca Binary files /dev/null and b/tests/auto/network/ssl/qsslkey/keys/dsa-pri-960.der differ diff --git a/tests/auto/network/ssl/qsslkey/keys/dsa-pri-960.pem b/tests/auto/network/ssl/qsslkey/keys/dsa-pri-960.pem new file mode 100644 index 0000000000..054c174ecf --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/keys/dsa-pri-960.pem @@ -0,0 +1,11 @@ +-----BEGIN DSA PRIVATE KEY----- +MIIBnwIBAAJ5AMDmMbjPuQOJOd57ZBya8tICsSbRGo+yStRm0rubqAnjKzgMOZc8 +CWT+/2ddVYKuQCzG0DEYeGyBxGUSP2Z7TQrVJ/4GmHLeHJIiIBT8JAh+yHmDWhNO +sKZbsUtsckwBEuh/1vs++L+PX8QGvHCE6Y+sFTR6xCs6EQIVAO/gigt7sEp3MzIh +cFGLiT6PlbNNAnh+GFk2cJecrMbpw3dT5ilTPZkWm8nyC/1fJwVk4dEdY7yjAnCX +Su1eO1m6RLM/OlVXMT84HKaqA95w09dg8s9LmlrKR5ROH6MXYoOweowFcd/PrJd7 +U3sGRN7cwzVEW8/PZODYJY0Ab0bhpjQYeMObQf61t3iREbUCeB6KXt9DouH7ccWX +keJM0Uf+eXCxPbnMwMsR6ae3F2G/YNlarqkKI3CpuwcKI1Hvq3iitznPPmZJrqlG +ywI+es5axqskbV/7NY6VcwaHVR3FcdSpksxCuG5zP3GEqVS7b3WPj98jBGMb0VB4 +CtK6CpxjC283/Yuy8QIUdyj4ssD0ezN3bhOJtr3fBGWjX6k= +-----END DSA PRIVATE KEY----- diff --git a/tests/auto/network/ssl/qsslkey/keys/dsa-pub-1024.der b/tests/auto/network/ssl/qsslkey/keys/dsa-pub-1024.der new file mode 100644 index 0000000000..efff9d27c3 Binary files /dev/null and b/tests/auto/network/ssl/qsslkey/keys/dsa-pub-1024.der differ diff --git a/tests/auto/network/ssl/qsslkey/keys/dsa-pub-1024.pem b/tests/auto/network/ssl/qsslkey/keys/dsa-pub-1024.pem new file mode 100644 index 0000000000..f39bc37814 --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/keys/dsa-pub-1024.pem @@ -0,0 +1,12 @@ +-----BEGIN PUBLIC KEY----- +MIIBtjCCASsGByqGSM44BAEwggEeAoGBAMEM3EaWjXZRMJnhLlfFx9Dykppgxo9g +KlwmptExDz1WyFk8W6xnJychMdDd+OEfLE99cXDMoWH1zG8lMbJaZrNAGPhg4bT0 +PM+zd+9GIjPnO7Vt0nSsfRUk0p1sOZ0MfYEqyw+uoiNCgs3CNAtwTkN2DZqzx1+D +qk+LtBWrb5TrAhUAwyFrnSVEkAlyRm7O1YSRXgjsfTcCgYB9FdIgxRa9+fAsgO65 +OGPkqEUowvSR3QHGtP0fR0gKLilO/sPIfs5aGsJL/Tblor8B6Gt9T5BKJx5Q/Fkt +LF7KKxKC3FYiiWnzOCPWEE9Rb+WkzA2AKGgp/wSBqAcdjhjT54ucvsP/kziEFf15 +AuEre29pQtuRbimNH6udhb1QQwOBhAACgYAhRYQ296sCvQlqjhTyj8/VFahXvXnV +2YuQ/2QFm0KHXbRXsmR2ectwNcaBvD87EZ3KRXQiSXzea6WycOw9YXcTmgzLUefH +aBkQ38973pmPIrO91HDJqVE0GLOIIbh796Y/9z7//BO4yAXQO9jcxeFKwiB2zYlu +ZOVDOiFFgqqbMg== +-----END PUBLIC KEY----- diff --git a/tests/auto/network/ssl/qsslkey/keys/dsa-pub-512.der b/tests/auto/network/ssl/qsslkey/keys/dsa-pub-512.der new file mode 100644 index 0000000000..5d60a6576a Binary files /dev/null and b/tests/auto/network/ssl/qsslkey/keys/dsa-pub-512.der differ diff --git a/tests/auto/network/ssl/qsslkey/keys/dsa-pub-512.pem b/tests/auto/network/ssl/qsslkey/keys/dsa-pub-512.pem new file mode 100644 index 0000000000..df5f99982e --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/keys/dsa-pub-512.pem @@ -0,0 +1,8 @@ +-----BEGIN PUBLIC KEY----- +MIHxMIGoBgcqhkjOOAQBMIGcAkEA2tY9+lNVCnDx//ciofnvm/OPWi7oMQ6XFFfX +rpjltIG+9eRQv0j4SqcwHpsqcaC4vjsbHFTDR0p8UyUAbrXzLwIVAOKRP6SgXHve +EJ//aztaVltEWqBBAkAj1tdgG3FQBxG5UJQGl53XsOOBJ9xz21RrrrlkqCO9G9Cl +NjmQ1XzrkBHije1MPb6iOLH1aPYiY6svkxaDMb88A0QAAkEAzobcabWo6sag70pa +K/ihZlSbd4EC5Hl1XjtC5sKFBkjLK3A79f9TobAwDiHzSxdiLNT0xKd+iOKoWZnA +j+TJ2A== +-----END PUBLIC KEY----- diff --git a/tests/auto/network/ssl/qsslkey/keys/dsa-pub-576.der b/tests/auto/network/ssl/qsslkey/keys/dsa-pub-576.der new file mode 100644 index 0000000000..8c67395210 Binary files /dev/null and b/tests/auto/network/ssl/qsslkey/keys/dsa-pub-576.der differ diff --git a/tests/auto/network/ssl/qsslkey/keys/dsa-pub-576.pem b/tests/auto/network/ssl/qsslkey/keys/dsa-pub-576.pem new file mode 100644 index 0000000000..3a594bfecb --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/keys/dsa-pub-576.pem @@ -0,0 +1,8 @@ +-----BEGIN PUBLIC KEY----- +MIIBCDCBuAYHKoZIzjgEATCBrAJJANeKU0IdVgDc/dVL6vrEjgZW8lMLrUyjYDhu +65sl8LaOmamwvozxISFjYaeIMPCloHnn2jijsjQpjnlj5rVRT1MYWt2i56jV+wIV +ANvg9uDO7mNdcuOWuT+xveysakC9Akg2q1Vi56KzKVR4T2KIS4yTH3bz2wwiWd80 +p+SylyOkM/fSIrxJIRYGugR1WY7lgWAI8E0mJDXKmajpGSvysDHnjdlSpSVSUNID +SwACSD9wBxobS/ySpKs+ks11XCPyZiMsf+LjUtT7nK5RBTG0PDEw+82MchouPc4Y +UPGR+Qf64GB7AxBYQpt/YhLXtFVCocZUt0HJBw== +-----END PUBLIC KEY----- diff --git a/tests/auto/network/ssl/qsslkey/keys/dsa-pub-960.der b/tests/auto/network/ssl/qsslkey/keys/dsa-pub-960.der new file mode 100644 index 0000000000..58c80e805e Binary files /dev/null and b/tests/auto/network/ssl/qsslkey/keys/dsa-pub-960.der differ diff --git a/tests/auto/network/ssl/qsslkey/keys/dsa-pub-960.pem b/tests/auto/network/ssl/qsslkey/keys/dsa-pub-960.pem new file mode 100644 index 0000000000..bcdc9e343e --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/keys/dsa-pub-960.pem @@ -0,0 +1,11 @@ +-----BEGIN PUBLIC KEY----- +MIIBmjCCARkGByqGSM44BAEwggEMAnkAwOYxuM+5A4k53ntkHJry0gKxJtEaj7JK +1GbSu5uoCeMrOAw5lzwJZP7/Z11Vgq5ALMbQMRh4bIHEZRI/ZntNCtUn/gaYct4c +kiIgFPwkCH7IeYNaE06wpluxS2xyTAES6H/W+z74v49fxAa8cITpj6wVNHrEKzoR +AhUA7+CKC3uwSnczMiFwUYuJPo+Vs00CeH4YWTZwl5ysxunDd1PmKVM9mRabyfIL +/V8nBWTh0R1jvKMCcJdK7V47WbpEsz86VVcxPzgcpqoD3nDT12Dyz0uaWspHlE4f +oxdig7B6jAVx38+sl3tTewZE3tzDNURbz89k4NgljQBvRuGmNBh4w5tB/rW3eJER +tQN7AAJ4Hope30Oi4ftxxZeR4kzRR/55cLE9uczAyxHpp7cXYb9g2VquqQojcKm7 +BwojUe+reKK3Oc8+ZkmuqUbLAj56zlrGqyRtX/s1jpVzBodVHcVx1KmSzEK4bnM/ +cYSpVLtvdY+P3yMEYxvRUHgK0roKnGMLbzf9i7Lx +-----END PUBLIC KEY----- diff --git a/tests/auto/network/ssl/qsslkey/keys/genkeys.sh b/tests/auto/network/ssl/qsslkey/keys/genkeys.sh new file mode 100755 index 0000000000..559ef5cf5d --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/keys/genkeys.sh @@ -0,0 +1,82 @@ +#!/bin/sh +############################################################################# +## +## Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +## All rights reserved. +## Contact: Nokia Corporation (qt-info@nokia.com) +## +## This file is the build configuration utility of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:LGPL$ +## GNU Lesser General Public License Usage +## This file may be used under the terms of the GNU Lesser General Public +## License version 2.1 as published by the Free Software Foundation and +## appearing in the file LICENSE.LGPL included in the packaging of this +## file. Please review the following information to ensure the GNU Lesser +## General Public License version 2.1 requirements will be met: +## http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +## +## In addition, as a special exception, Nokia gives you certain additional +## rights. These rights are described in the Nokia Qt LGPL Exception +## version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU General +## Public License version 3.0 as published by the Free Software Foundation +## and appearing in the file LICENSE.GPL included in the packaging of this +## file. Please review the following information to ensure the GNU General +## Public License version 3.0 requirements will be met: +## http://www.gnu.org/copyleft/gpl.html. +## +## Other Usage +## Alternatively, this file may be used in accordance with the terms and +## conditions contained in a signed written agreement between you and Nokia. +## +## +## +## +## +## $QT_END_LICENSE$ +## +############################################################################# + +# This script generates cryptographic keys of different types. + +#--- RSA --------------------------------------------------------------------------- +# Note: RSA doesn't require the key size to be divisible by any particular number +for size in 40 511 512 999 1023 1024 2048 +do + echo -e "\ngenerating RSA private key to PEM file ..." + openssl genrsa -out rsa-pri-$size.pem $size + + echo -e "\ngenerating RSA private key to DER file ..." + openssl rsa -in rsa-pri-$size.pem -out rsa-pri-$size.der -outform DER + + echo -e "\ngenerating RSA public key to PEM file ..." + openssl rsa -in rsa-pri-$size.pem -pubout -out rsa-pub-$size.pem + + echo -e "\ngenerating RSA public key to DER file ..." + openssl rsa -in rsa-pri-$size.pem -pubout -out rsa-pub-$size.der -outform DER +done + +#--- DSA ---------------------------------------------------------------------------- +# Note: DSA requires the key size to be in interval [512, 1024] and be divisible by 64 +for size in 512 576 960 1024 +do + echo -e "\ngenerating DSA parameters to PEM file ..." + openssl dsaparam -out dsapar-$size.pem $size + + echo -e "\ngenerating DSA private key to PEM file ..." + openssl gendsa dsapar-$size.pem -out dsa-pri-$size.pem + + /bin/rm dsapar-$size.pem + + echo -e "\ngenerating DSA private key to DER file ..." + openssl dsa -in dsa-pri-$size.pem -out dsa-pri-$size.der -outform DER + + echo -e "\ngenerating DSA public key to PEM file ..." + openssl dsa -in dsa-pri-$size.pem -pubout -out dsa-pub-$size.pem + + echo -e "\ngenerating DSA public key to DER file ..." + openssl dsa -in dsa-pri-$size.pem -pubout -out dsa-pub-$size.der -outform DER +done diff --git a/tests/auto/network/ssl/qsslkey/keys/rsa-pri-1023.der b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-1023.der new file mode 100644 index 0000000000..d23dde8058 Binary files /dev/null and b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-1023.der differ diff --git a/tests/auto/network/ssl/qsslkey/keys/rsa-pri-1023.pem b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-1023.pem new file mode 100644 index 0000000000..bd1c119217 --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-1023.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICWQIBAAKBgF0LbcbxSbBWA+7N9kaZ1OHRChPCN74g69SuV29p+CkRcgReeZHB +LkEcTERBUn5hw4AlgbmbVEFplLs/bbqS7hOK/Pkz9TCM9wG2j8T2mcPIi8NU8RMc +oNyVINlVduKxSID491sRZuQXLX+CM7klIA7rpcdsfefpuhPu0yTCJ1wJAgMBAAEC +gYAVxvYDqTfREODMxOIOdjTQQaf1JfD8DCKGXePTDN9rZUzlXuNfTPKxhJkJJKzV +LbafSKgLNCLNSJPqG5doIrmcCU4r4qVhjyQtTQ8orFaIKjWgrvIUXh8DIioKOD3o +ikiJvg0A7oL4+F/jHqpeUzzlNOyGRRsNP95DJUor1UuT3QJBAMD7qNUXYxDLtjs9 +VFRRN7KkUvBz4FXD+XwgE1ZrYQEy7IN/ZaHeFGb/59WbQc5LEwDY5N+GFlwWm0pQ +6iZdj4cCQHttdWhXcutPDZOUs6JHq8Q6q9j8USGyhmJ3nOKnbGOLhimvlceW+P0n +AjGnFhicIdr7sht1HgacsfHxT+WD++8CQGQDyRsu9LOmr/oBrevI79HQof9mPpSU +bX8r7Hm0SuDhMHxyEQ0T2IUNCWzF+xPcCqw8vFkv1/S/7plUWBWsFY8CQGnrFMXl +xYiI3m23PjAUsSJD9xTN8hGo0gnN+6PFaQ5DvW4ob7BHroQP5aIWcK/NzH/PXpiR +eKLT9SBapOWgzaECQFp8lWNJpW4dK2w0XafRB3fwQ/OOvv4EWTOCsbHEmNg/RgIG +HKNAZekwBn9lfnYRaKC7z9VtTEy6mmMnaLE6Srw= +-----END RSA PRIVATE KEY----- diff --git a/tests/auto/network/ssl/qsslkey/keys/rsa-pri-1024.der b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-1024.der new file mode 100644 index 0000000000..0ea20598f0 Binary files /dev/null and b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-1024.der differ diff --git a/tests/auto/network/ssl/qsslkey/keys/rsa-pri-1024.pem b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-1024.pem new file mode 100644 index 0000000000..f2fc5b0064 --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-1024.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQDN8qIMJKzoi0rUWBYx7ggiUATFLITKbHQ+sdeb3JKwzHv4pLY1 +YNsQWd+RD1tLUN5qJNrj4b8GWyJ4pipxr9Dq0Ak5b0Qvhuell5a8JVrUSRvsfUvc +VlNY6yv7JZKetCYmCEF/IkRc4RegS8bEOU4lddN3RotDsu6eZ7/sgyPd0wIDAQAB +AoGAIXeecc678oZAQMWXxcFivQC35brYUXTSZyXLXd64T9bLBprtqTr2HnAcbpxK +P7ECk8ktcP7MZiIFNgn6t8LUdik34iMdkzqAG4w4g/qET1ANd+cvGs6Ct5b+82HY +7s4sGXsOyU9BKltHrjSSEn3EIhC58GZQpJADNoiBb63f5kECQQD50SNoeb3LcFs/ +peZNcbpm7yR3BEkDYm94iENmP/Su4Y38OJ/cArmEODj/pg6UAgtq6r5ARjfQUKkz +G+WD421jAkEA0wuJMMRtNh8ellc7BEbmVpgWCOZFjDXAyH0atv+THDnhq5bs4PNh +ldc+PDVO6b3BXxanzrfH19qE9P1xTscw0QJABPKhcuZGXgsho4cToqEIAhXmcP0C +vTBr6MU6IIJPxE7vUvmEIQVkyzuZ1WZij4fi1gFEN12sqVGPqFV7HDua5QJADfTf +V7hFuKSKM0sFfhzqMqPQITqJMuKDIz8SuzNfmShCwkQy2hV+SJh5ZyXnWBHSbeVj +p4oDz0SGMmVoTtthYQJBAOJeHHuQ7M/rZjHWRiVFnyAh/WfBdD1d/4JgfjsHJUEp +yCfMpEw5Nb1vBdlhKIn1tTBUMX5WCeQaZWIi1g9OAnU= +-----END RSA PRIVATE KEY----- diff --git a/tests/auto/network/ssl/qsslkey/keys/rsa-pri-2048.der b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-2048.der new file mode 100644 index 0000000000..194da2adf2 Binary files /dev/null and b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-2048.der differ diff --git a/tests/auto/network/ssl/qsslkey/keys/rsa-pri-2048.pem b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-2048.pem new file mode 100644 index 0000000000..125d281792 --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-2048.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAuwQ1KqA3hewLXsG9fHD7UI3fQu5ylQiDeRWEwsFovmiDfDNo +7P/VuByvQmVR30p3GzNnZdMsj8jVlLD/Wny1nU/O2T00KQ95SJhiEGGCVWirRf/7 +F1KrFGP7HuiQ76FQwqtRxBDQw3QOm2PXp20EpyzGdYpY13EkY6n0FLumWoJxL+2g +TW0ECyLulxXFK9wXCNWnY+ji5V8VwDfkXk7WXYzMawnFnKNiSJ99jkfJ28lo+Ba8 +LAfvHM6ygeudWndri6pGkA/R0Z3knca6yz5YJCMph5AKgj9beOaLsIWEbdbmZA7f +zuu8QhBK6cjvjUbS6iPVDEfBnI6TOpGPJxvWNwIDAQABAoIBAE5bZfdVJ85Z5y2t +mToBkJrgxpGhnKqq5YMfK//5z2wwGB863xSl4KMCicAU+afH6H2iHQmB4GLiL/Lb +8jG0nNerr6w/w5IMpLFw/22DnXsxnsl2hnrwLTQe7AG4SbnNaiwWYez3arDp+JDM +1XeV6uQsk35+F9k0051agSUKtpmm5Be9bu6lW8a55gJsAvUIrj0eLxXN7nxhI6tL +W1fwylJUX0ACka5SMsFOue7UPxPEGpM2OHI64T3QWpgoaXwyfNyn22u4PHOiWOtt +io/r5Ta61mf8FiSV8X33/nm/yjUpW2EKJuIxA8IWcrADTyQrO5AKq0MFJjQVy8I+ +jrx0pzkCgYEA6LJp/slfTiz6Y9b5GVFY48QL+msorvjuyPdGwEkkCGAjo8E24Lac +91T2Bx3XKxTrqyu8FDwa0RQzXresM0Tqvi3WT/oIauuiZL2U3ef32QQ42tvOWmGM +09hES8Ymx78xFUzh20AdzAfLGaxvQ4Z19yt/aGmZH1/Giyc+TO8rBusCgYEAzb6y +FMQSYvnvQTLI2qUt1h/kjU01JdCkGRgfMyyAW/pYq56vPJBKvsnrH0rDmICY3Cam +Ma4hf7HekfqbIyYw+cklxZHYmVPf16T9Tyopwwltf8k9mULwnsSY7Cti2VBGcAGs +DbgYz9HoJERe2qfgWKuTGXMHTXh5+x28bncecuUCgYAZGITN8bHRoJAwjFK5JEjN +56rTCxOMK1S1X9XFjx3PuW6ejov4mDk9g3A1PEakYtE32LOkCwdpXSBdKrnrnZcj +g5YFchXOUjNlYfzYIdhdE1N+Zw5OOQ8TGdPjyC8Og9QrU+tX1gZgpsaEslAdnDbE +vXA0No9JDu8+urDBY1nkmQKBgCxwErCPhNUh3H49hMpMwPCtpMSsN116iebtodeA +thynnr06oei31Z0NqZ3xnPt0Uw/BvUWfsZaOCV+IPDfESPG6c1EP5jN/vSordZbC +qZB5mc/u3EIdBjGUjt77o/cB8qTAh7PS3nEMilJC/iXF+pvMOxJVGO+vpNELJCbL +zaTNAoGAQ7qhJUUWENzWbuh1g/bYy/980/nhE24iWGwQgFeQOrUnghBLQvUUp7a0 +kwNqZmlxl97HCxusM2T/lZoPvMBWhzfszzbaZchiOWIkbUXiHTrVsRnBHysHTpD+ +bsHGs9ILGSEYymWOErGII5wZ5DJOVUmtzCrgRvyZfxbH/lOZSZc= +-----END RSA PRIVATE KEY----- diff --git a/tests/auto/network/ssl/qsslkey/keys/rsa-pri-40.der b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-40.der new file mode 100644 index 0000000000..227cbbba00 Binary files /dev/null and b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-40.der differ diff --git a/tests/auto/network/ssl/qsslkey/keys/rsa-pri-40.pem b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-40.pem new file mode 100644 index 0000000000..e480b11029 --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-40.pem @@ -0,0 +1,4 @@ +-----BEGIN RSA PRIVATE KEY----- +MC8CAQACBgC+dq4BGQIDAQABAgVOUamohQIDDtUPAgMM11cCAnzbAgMA350CAwIz +jw== +-----END RSA PRIVATE KEY----- diff --git a/tests/auto/network/ssl/qsslkey/keys/rsa-pri-511.der b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-511.der new file mode 100644 index 0000000000..43da25456e Binary files /dev/null and b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-511.der differ diff --git a/tests/auto/network/ssl/qsslkey/keys/rsa-pri-511.pem b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-511.pem new file mode 100644 index 0000000000..70872d9814 --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-511.pem @@ -0,0 +1,9 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIBOAIBAAJAVFNCgrdkp5mE62MBco89Ip/tbJdHq09d8Q72Kw0SERi8zoXewZR7 +NwlcAns+boTWCzgRfIs051qZGzao9o+jaQIDAQABAkA111ePSPOew7oH5ZZGBElf +s0eQX2NmcW2k/g2V3Wmcui+ejz9edzsooCXYBiLtfAIHNq0MwOLkl6kmxfoeGgAV +AiEAxnr8EPGStCvtIf4IFKHZdbl/+FanTVec0WqLPW+U2vMCIGzDPosDi74f30vi +78PfdSgy+pp4Sp/0pSttHBrQfyczAiEAkRTa5gO/ScYGG1/WRkacb2o9+ahVuLo/ +HfC0gR7r/zMCIBi+53j7iozsdYKvlHvNWLuXtbr1iY0RPY8+2Pkk3PlPAiB3CDCM +Pu1PssfpwYB0Lr68Mnx1oiZPLZje5HAVzptD6A== +-----END RSA PRIVATE KEY----- diff --git a/tests/auto/network/ssl/qsslkey/keys/rsa-pri-512.der b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-512.der new file mode 100644 index 0000000000..156a1d1439 Binary files /dev/null and b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-512.der differ diff --git a/tests/auto/network/ssl/qsslkey/keys/rsa-pri-512.pem b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-512.pem new file mode 100644 index 0000000000..dda274f50c --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-512.pem @@ -0,0 +1,9 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIBPAIBAAJBAOyVSr5qVl/cuVAbjSs1cLFi17mJXg8X9vQC9xb4BjHUY9i60vIV +zAaE9knwaNkRwPfxgzp244WGFzXRO7UwHKECAwEAAQJBAOCAMvNKmThSjg90dZuh +FJVfxHIt5nHQPeoDvlGvfsFchWLI42451QTKbXFlSBLb2F4kCVrzLZN5zrGfcagy +pgECIQD4pPthJoUmQVCLqMJKgtFPzfwSetxYe8+BFeBKupSz8QIhAPOU998ZEmzB +YPajTzmUK9SIxk88m93O5GSqC+HS4eOxAiBHbM0xaSlSheynUcEOOmn6LMe+X0Cw +wlAnWqtljBdvYQIhAJiIoPBEcxVHw8CvY91avFfLjuPvwGuWM9AV7Hrt5CcBAiEA +ykI2wY34X9/1rodR/6CXUW5cJq7TCHt8bAsowkXH7bU= +-----END RSA PRIVATE KEY----- diff --git a/tests/auto/network/ssl/qsslkey/keys/rsa-pri-999.der b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-999.der new file mode 100644 index 0000000000..3b3f8ca6d5 Binary files /dev/null and b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-999.der differ diff --git a/tests/auto/network/ssl/qsslkey/keys/rsa-pri-999.pem b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-999.pem new file mode 100644 index 0000000000..6ed72bf1f9 --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-999.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICSwIBAAJ9ZTk4kN4YU/14BJi/Fsa0cgMnN7hzZIGD0Tw5kzTBh2SKPkd5TkaM +saWAsI+ZBICDTxEAUJ4y7KW/QchROTjRI04HKBXa30rk51WQ98W9/AEN4+roWeA9 +YQ3V1nIu1lYA+Ypb4/kBW1VI9yeOTxh4QD1zyrBP3CQ1wQrhth8CAwEAAQJ9El4T +uZXTbEouIQdmhUeBLr8Lurypk688sBbxi5ijFWJVA4AMrvt2RDX36BHmfj6jlRFM +sVRDgVWCIvmqY2u8I0YuHoLRc7Xsg6ICQljxz0H5frs2q4suc2AHEbxBstlUWHSQ +fYDyNq/b1xf5+LOCeU6VP9z3ZwtHydACgYECPwzrRsj4dMIs2/i6Vs0Epa8gf4Oc +kw+e1sjigFkvpA/iDJl39wG+lJQkYEfJEY2cfCCIBreof9sKtq6SP3a3kQI/B9XQ +nsjhw71h6lV/dOUFRmSYV7fJDIIqZEA+NvO24kRjbSagKXNKFWRgq/SRzbT9Z4vC +WPhhCYbi6zeU/ZqvAj8Bi/FPgh9IV+KF5pkZtmK3kqZxdT9N8XA5YJSJGOEFXcSw +YuJZfR2AltJIBdRqJb/ejSpwtmWILcItKCkc52ECPwM4rKJsEy6IMbbH2jR0aNvN +Rjlibb2/0rAPHcU4JKwY1AwvglAof4AWGgz46+14iWtGMeykijkBhVCSNKakLwI/ +Bjzrf1iLEl3ylN47Cukse7sKSXI58tUaIJprjihMO/64dVK1ZdkXVGLMft9Eq+7l +wTVe2ajpMWFm9wAD8114 +-----END RSA PRIVATE KEY----- diff --git a/tests/auto/network/ssl/qsslkey/keys/rsa-pub-1023.der b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-1023.der new file mode 100644 index 0000000000..4b16fdddd0 Binary files /dev/null and b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-1023.der differ diff --git a/tests/auto/network/ssl/qsslkey/keys/rsa-pub-1023.pem b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-1023.pem new file mode 100644 index 0000000000..174a135514 --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-1023.pem @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgF0LbcbxSbBWA+7N9kaZ1OHRChPC +N74g69SuV29p+CkRcgReeZHBLkEcTERBUn5hw4AlgbmbVEFplLs/bbqS7hOK/Pkz +9TCM9wG2j8T2mcPIi8NU8RMcoNyVINlVduKxSID491sRZuQXLX+CM7klIA7rpcds +fefpuhPu0yTCJ1wJAgMBAAE= +-----END PUBLIC KEY----- diff --git a/tests/auto/network/ssl/qsslkey/keys/rsa-pub-1024.der b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-1024.der new file mode 100644 index 0000000000..96495920d0 Binary files /dev/null and b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-1024.der differ diff --git a/tests/auto/network/ssl/qsslkey/keys/rsa-pub-1024.pem b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-1024.pem new file mode 100644 index 0000000000..d5c8da9144 --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-1024.pem @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDN8qIMJKzoi0rUWBYx7ggiUATF +LITKbHQ+sdeb3JKwzHv4pLY1YNsQWd+RD1tLUN5qJNrj4b8GWyJ4pipxr9Dq0Ak5 +b0Qvhuell5a8JVrUSRvsfUvcVlNY6yv7JZKetCYmCEF/IkRc4RegS8bEOU4lddN3 +RotDsu6eZ7/sgyPd0wIDAQAB +-----END PUBLIC KEY----- diff --git a/tests/auto/network/ssl/qsslkey/keys/rsa-pub-2048.der b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-2048.der new file mode 100644 index 0000000000..560487b67e Binary files /dev/null and b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-2048.der differ diff --git a/tests/auto/network/ssl/qsslkey/keys/rsa-pub-2048.pem b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-2048.pem new file mode 100644 index 0000000000..95daf33896 --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-2048.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuwQ1KqA3hewLXsG9fHD7 +UI3fQu5ylQiDeRWEwsFovmiDfDNo7P/VuByvQmVR30p3GzNnZdMsj8jVlLD/Wny1 +nU/O2T00KQ95SJhiEGGCVWirRf/7F1KrFGP7HuiQ76FQwqtRxBDQw3QOm2PXp20E +pyzGdYpY13EkY6n0FLumWoJxL+2gTW0ECyLulxXFK9wXCNWnY+ji5V8VwDfkXk7W +XYzMawnFnKNiSJ99jkfJ28lo+Ba8LAfvHM6ygeudWndri6pGkA/R0Z3knca6yz5Y +JCMph5AKgj9beOaLsIWEbdbmZA7fzuu8QhBK6cjvjUbS6iPVDEfBnI6TOpGPJxvW +NwIDAQAB +-----END PUBLIC KEY----- diff --git a/tests/auto/network/ssl/qsslkey/keys/rsa-pub-40.der b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-40.der new file mode 100644 index 0000000000..3d789a7e11 Binary files /dev/null and b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-40.der differ diff --git a/tests/auto/network/ssl/qsslkey/keys/rsa-pub-40.pem b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-40.pem new file mode 100644 index 0000000000..f053580d90 --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-40.pem @@ -0,0 +1,3 @@ +-----BEGIN PUBLIC KEY----- +MCEwDQYJKoZIhvcNAQEBBQADEAAwDQIGAL52rgEZAgMBAAE= +-----END PUBLIC KEY----- diff --git a/tests/auto/network/ssl/qsslkey/keys/rsa-pub-511.der b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-511.der new file mode 100644 index 0000000000..2da3341554 Binary files /dev/null and b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-511.der differ diff --git a/tests/auto/network/ssl/qsslkey/keys/rsa-pub-511.pem b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-511.pem new file mode 100644 index 0000000000..a5135f0e4c --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-511.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFswDQYJKoZIhvcNAQEBBQADSgAwRwJAVFNCgrdkp5mE62MBco89Ip/tbJdHq09d +8Q72Kw0SERi8zoXewZR7NwlcAns+boTWCzgRfIs051qZGzao9o+jaQIDAQAB +-----END PUBLIC KEY----- diff --git a/tests/auto/network/ssl/qsslkey/keys/rsa-pub-512.der b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-512.der new file mode 100644 index 0000000000..372ba8f8b5 Binary files /dev/null and b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-512.der differ diff --git a/tests/auto/network/ssl/qsslkey/keys/rsa-pub-512.pem b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-512.pem new file mode 100644 index 0000000000..d3a5e5882f --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-512.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAOyVSr5qVl/cuVAbjSs1cLFi17mJXg8X +9vQC9xb4BjHUY9i60vIVzAaE9knwaNkRwPfxgzp244WGFzXRO7UwHKECAwEAAQ== +-----END PUBLIC KEY----- diff --git a/tests/auto/network/ssl/qsslkey/keys/rsa-pub-999.der b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-999.der new file mode 100644 index 0000000000..1d28000501 Binary files /dev/null and b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-999.der differ diff --git a/tests/auto/network/ssl/qsslkey/keys/rsa-pub-999.pem b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-999.pem new file mode 100644 index 0000000000..00ba47915a --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-999.pem @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIGaMA0GCSqGSIb3DQEBAQUAA4GIADCBhAJ9ZTk4kN4YU/14BJi/Fsa0cgMnN7hz +ZIGD0Tw5kzTBh2SKPkd5TkaMsaWAsI+ZBICDTxEAUJ4y7KW/QchROTjRI04HKBXa +30rk51WQ98W9/AEN4+roWeA9YQ3V1nIu1lYA+Ypb4/kBW1VI9yeOTxh4QD1zyrBP +3CQ1wQrhth8CAwEAAQ== +-----END PUBLIC KEY----- diff --git a/tests/auto/network/ssl/qsslkey/qsslkey.pro b/tests/auto/network/ssl/qsslkey/qsslkey.pro new file mode 100644 index 0000000000..5a90b76219 --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/qsslkey.pro @@ -0,0 +1,32 @@ +load(qttest_p4) + +SOURCES += tst_qsslkey.cpp +!wince*:win32:LIBS += -lws2_32 +QT = core network + +TARGET = tst_qsslkey + +win32 { + CONFIG(debug, debug|release) { + DESTDIR = debug +} else { + DESTDIR = release + } +} + +wince*|symbian: { + keyFiles.files = keys + keyFiles.path = . + + passphraseFiles.files = rsa-without-passphrase.pem rsa-with-passphrase.pem + passphraseFiles.path = . + + DEPLOYMENT += keyFiles passphraseFiles +} + +wince*: { + DEFINES += SRCDIR=\\\".\\\" +} else:!symbian { + DEFINES+= SRCDIR=\\\"$$PWD\\\" + TARGET.CAPABILITY = NetworkServices +} diff --git a/tests/auto/network/ssl/qsslkey/rsa-with-passphrase.pem b/tests/auto/network/ssl/qsslkey/rsa-with-passphrase.pem new file mode 100644 index 0000000000..cb29becc31 --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/rsa-with-passphrase.pem @@ -0,0 +1,18 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-CBC,36BD1242254C5E1E + +sqt8qlQfkjJiz0djd0WYjhN/IGxA/nU/wVKuf5RWDAghDBrABzJ3dU4Jh1WIsS4+ +f22TBr6fwgjpPxGkt361Z9oxon/QeyBZLFtyUxnFSqZcVMMR3vndtMyYJbVKjRm1 +lvF3BjFWNh6+SZe20cut2GiUJDqhw7RbjaAN6LaCpFqwusY6vbjW6vzB8ezDvLou +5jQAkwArGoI0KqUMwBOYukiWdBA0iERavspKGRnB3mGtgv5ziTEFzx58mn1Lv3Qs +LYQqTYgzpFyAMP9SZaRv4m/y5O9foAXnlh0GhmDWBQ2D5flwZqrIAzoJ5BcZKU6/ +HJCh4snw3kheeE8NhrlzypEONedvu4ifUbqN5idMU7S4t40NAmQ/dF0Z4wDen/M/ +iFbt5tTWh6sXK82XzJtAfprH07odtJHK7CMeurCi5BupmnLtPbUrl6hpKItBzu+g +7MB5AyNk548V9Y8+kKBtEG5EgYZrMYX4yqQ+Z8F1hy0UUMXu9cAnO06OTavxLtWJ +ikmwYJNy421Hj+oZVSagCUILQyUfgx6fXWwDRqy/stlX+hpPPjVmd/A2WBm5x/Sf +5CGfUtddZRuAZpChBXV6a/R+nMzDXhkKl4XTkN8hg3yXLY6xy3CR3RIYDlKkn85y +VziP32V6Bc8ucGifsZLNnvj8CFXTZP+8CWun9yLSkcq+wm4cQOLswztEMA8bbPJQ +g7Gp59BC4ofN5bMZ1R1z+l96x+YMY9btkyjE1uEyRT88dHwxnkhC5AKBx2P6sg0C +doe5Dh8Ny5Ic24ibwyvZbAS46tSVdha7ACGnGXV4Z3iqBfN0b0UNmw== +-----END RSA PRIVATE KEY----- diff --git a/tests/auto/network/ssl/qsslkey/rsa-without-passphrase.pem b/tests/auto/network/ssl/qsslkey/rsa-without-passphrase.pem new file mode 100644 index 0000000000..f7c834b116 --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/rsa-without-passphrase.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQC1mZlLC6g8/vaw8XEOQ59gwQ5WxIvcKCSx7B5SHaeN8zzbENpl +32BtyfrCccHF7j6qwfMZ2RwM5RTlFw/eBF4SSXAgp/P5CgcugSs1dOJUfPveos/5 +h3fmnUcKeQIU4m5EGcQicmR8//WUUfdtHDp/fJ0dRXcY2tTOx89vNPEtgwIDAQAB +AoGBAKqE2f4vqf/sYPPxInmEYclWPgKXd8R4JUy0LBxrIAQYXBJPluOFhmRQ/hdK +/eq/NTTd/UlOJhqtmJsstoeAjlsELl2AejX2n1B9aSffQ0WzdB5gVMNotPGRKRIG +eOq2pp2JOFkGps11LUAqDEMNUb3EV6HiBucoGEOUpdITLrRxAkEA4Ul3o44wMvIb +muwp7/erSvDMWRZ1GjksEmBMAHL6y8avZd9UgrjHeCy8uHXrT0id4Cig8FA+nQA5 +UwTr9y5e9wJBAM5bc8xuAuCg4Si3exssFfxQIxFTmPzhkVubglO9IcLqfXLl3k5S +CxgRb/4pBMKVRCMRXAkaZpjJqTIofjp4ptUCQQC4hySnot932zchPi9bjtGPII1A +q1RfllSy+I1IEOW745HnL3ZZXGCF3p71uCB1YFVwNdcc/51Jm9VYWr+sRx8hAkBL +KoTDsk7aA8AAVNVC0Iwxm/8qEIlpk8Ce3cZbOklR9pg7gf+4B6qC2dcxfT9+oWBw +ZaJgrn0wqkQ3QQi5w7kBAkBAR2tKc2OqnljMPnXYEreRyHHjhqCsJYFAE7u16cY/ +NQGJq9jBAD5WANclrYRxKtD6yohi+Y7Vi7+SXve3pGdF +-----END RSA PRIVATE KEY----- diff --git a/tests/auto/network/ssl/qsslkey/tst_qsslkey.cpp b/tests/auto/network/ssl/qsslkey/tst_qsslkey.cpp new file mode 100644 index 0000000000..ac966ce74a --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/tst_qsslkey.cpp @@ -0,0 +1,447 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include +#include + +#include +#include + +#ifdef Q_OS_SYMBIAN +// In Symbian OS test data is located in applications private dir +// Current path (C:\private\) contains only ascii chars +#define SRCDIR "." +#endif + +class tst_QSslKey : public QObject +{ + Q_OBJECT + + struct KeyInfo { + QFileInfo fileInfo; + QSsl::KeyAlgorithm algorithm; + QSsl::KeyType type; + int length; + QSsl::EncodingFormat format; + KeyInfo( + const QFileInfo &fileInfo, QSsl::KeyAlgorithm algorithm, QSsl::KeyType type, + int length, QSsl::EncodingFormat format) + : fileInfo(fileInfo), algorithm(algorithm), type(type), length(length) + , format(format) {} + }; + + QList keyInfoList; + + void createPlainTestRows(); + +public: + tst_QSslKey(); + virtual ~tst_QSslKey(); + +public slots: + void initTestCase_data(); + void init(); + void cleanup(); + +#ifndef QT_NO_OPENSSL + +private slots: + void emptyConstructor(); + void constructor_data(); + void constructor(); + void copyAndAssign_data(); + void copyAndAssign(); + void equalsOperator(); + void length_data(); + void length(); + void toPemOrDer_data(); + void toPemOrDer(); + void toEncryptedPemOrDer_data(); + void toEncryptedPemOrDer(); + + void passphraseChecks(); +#endif +}; + +tst_QSslKey::tst_QSslKey() +{ +#ifdef Q_WS_MAC + // applicationDirPath() points to a path inside the app bundle on Mac. + QDir dir(qApp->applicationDirPath() + QLatin1String("/../../../keys")); +#else + QDir dir(SRCDIR + QLatin1String("/keys")); // prefer this way to avoid ifdeffery and support shadow builds? +#endif + QFileInfoList fileInfoList = dir.entryInfoList(QDir::Files | QDir::Readable); + QRegExp rx(QLatin1String("^(rsa|dsa)-(pub|pri)-(\\d+)\\.(pem|der)$")); + foreach (QFileInfo fileInfo, fileInfoList) { + if (rx.indexIn(fileInfo.fileName()) >= 0) + keyInfoList << KeyInfo( + fileInfo, + rx.cap(1) == QLatin1String("rsa") ? QSsl::Rsa : QSsl::Dsa, + rx.cap(2) == QLatin1String("pub") ? QSsl::PublicKey : QSsl::PrivateKey, + rx.cap(3).toInt(), + rx.cap(4) == QLatin1String("pem") ? QSsl::Pem : QSsl::Der); + } +} + +tst_QSslKey::~tst_QSslKey() +{ +} + +void tst_QSslKey::initTestCase_data() +{ +} + +void tst_QSslKey::init() +{ +} + +void tst_QSslKey::cleanup() +{ +} + +static QByteArray readFile(const QString &absFilePath) +{ + QFile file(absFilePath); + if (!file.open(QIODevice::ReadOnly)) { + QWARN("failed to open file"); + return QByteArray(); + } + return file.readAll(); +} + +#ifndef QT_NO_OPENSSL + +void tst_QSslKey::emptyConstructor() +{ + if (!QSslSocket::supportsSsl()) + return; + + QSslKey key; + QVERIFY(key.isNull()); + QVERIFY(key.length() < 0); + + QSslKey key2; + QCOMPARE(key, key2); +} + +Q_DECLARE_METATYPE(QSsl::KeyAlgorithm) +Q_DECLARE_METATYPE(QSsl::KeyType) +Q_DECLARE_METATYPE(QSsl::EncodingFormat) + +void tst_QSslKey::createPlainTestRows() +{ + QTest::addColumn("absFilePath"); + QTest::addColumn("algorithm"); + QTest::addColumn("type"); + QTest::addColumn("length"); + QTest::addColumn("format"); + foreach (KeyInfo keyInfo, keyInfoList) { + QTest::newRow(keyInfo.fileInfo.fileName().toLatin1()) + << keyInfo.fileInfo.absoluteFilePath() << keyInfo.algorithm << keyInfo.type + << keyInfo.length << keyInfo.format; + } +} + +void tst_QSslKey::constructor_data() +{ + createPlainTestRows(); +} + +void tst_QSslKey::constructor() +{ + if (!QSslSocket::supportsSsl()) + return; + + QFETCH(QString, absFilePath); + QFETCH(QSsl::KeyAlgorithm, algorithm); + QFETCH(QSsl::KeyType, type); + QFETCH(QSsl::EncodingFormat, format); + + QByteArray encoded = readFile(absFilePath); + QSslKey key(encoded, algorithm, format, type); + QVERIFY(!key.isNull()); +} + +void tst_QSslKey::copyAndAssign_data() +{ + createPlainTestRows(); +} + +void tst_QSslKey::copyAndAssign() +{ + if (!QSslSocket::supportsSsl()) + return; + + QFETCH(QString, absFilePath); + QFETCH(QSsl::KeyAlgorithm, algorithm); + QFETCH(QSsl::KeyType, type); + QFETCH(QSsl::EncodingFormat, format); + + QByteArray encoded = readFile(absFilePath); + QSslKey key(encoded, algorithm, format, type); + + QSslKey copied(key); + QCOMPARE(key, copied); + QCOMPARE(key.algorithm(), copied.algorithm()); + QCOMPARE(key.type(), copied.type()); + QCOMPARE(key.length(), copied.length()); + QCOMPARE(key.toPem(), copied.toPem()); + QCOMPARE(key.toDer(), copied.toDer()); + + QSslKey assigned = key; + QCOMPARE(key, assigned); + QCOMPARE(key.algorithm(), assigned.algorithm()); + QCOMPARE(key.type(), assigned.type()); + QCOMPARE(key.length(), assigned.length()); + QCOMPARE(key.toPem(), assigned.toPem()); + QCOMPARE(key.toDer(), assigned.toDer()); +} + +void tst_QSslKey::equalsOperator() +{ + // ### unimplemented +} + +void tst_QSslKey::length_data() +{ + createPlainTestRows(); +} + +void tst_QSslKey::length() +{ + if (!QSslSocket::supportsSsl()) + return; + + QFETCH(QString, absFilePath); + QFETCH(QSsl::KeyAlgorithm, algorithm); + QFETCH(QSsl::KeyType, type); + QFETCH(int, length); + QFETCH(QSsl::EncodingFormat, format); + + QByteArray encoded = readFile(absFilePath); + QSslKey key(encoded, algorithm, format, type); + QVERIFY(!key.isNull()); + QCOMPARE(key.length(), length); +} + +void tst_QSslKey::toPemOrDer_data() +{ + createPlainTestRows(); +} + +void tst_QSslKey::toPemOrDer() +{ + if (!QSslSocket::supportsSsl()) + return; + + QFETCH(QString, absFilePath); + QFETCH(QSsl::KeyAlgorithm, algorithm); + QFETCH(QSsl::KeyType, type); + QFETCH(QSsl::EncodingFormat, format); + + QByteArray encoded = readFile(absFilePath); + QSslKey key(encoded, algorithm, format, type); + QVERIFY(!key.isNull()); + if (format == QSsl::Pem) + encoded.replace('\r', ""); + QCOMPARE(format == QSsl::Pem ? key.toPem() : key.toDer(), encoded); +} + +void tst_QSslKey::toEncryptedPemOrDer_data() +{ + QTest::addColumn("absFilePath"); + QTest::addColumn("algorithm"); + QTest::addColumn("type"); + QTest::addColumn("format"); + QTest::addColumn("password"); + + QStringList passwords; + passwords << " " << "foobar" << "foo bar" + << "aAzZ`1234567890-=~!@#$%^&*()_+[]{}\\|;:'\",.<>/?"; // ### add more (?) + foreach (KeyInfo keyInfo, keyInfoList) { + foreach (QString password, passwords) { + QString testName = QString("%1-%2-%3-%4").arg(keyInfo.fileInfo.fileName()) + .arg(keyInfo.algorithm == QSsl::Rsa ? "RSA" : "DSA") + .arg(keyInfo.type == QSsl::PrivateKey ? "PrivateKey" : "PublicKey") + .arg(keyInfo.format == QSsl::Pem ? "PEM" : "DER"); + QTest::newRow(testName.toLatin1()) + << keyInfo.fileInfo.absoluteFilePath() << keyInfo.algorithm << keyInfo.type + << keyInfo.format << password; + } + } +} + +void tst_QSslKey::toEncryptedPemOrDer() +{ + if (!QSslSocket::supportsSsl()) + return; + + QFETCH(QString, absFilePath); + QFETCH(QSsl::KeyAlgorithm, algorithm); + QFETCH(QSsl::KeyType, type); + QFETCH(QSsl::EncodingFormat, format); + QFETCH(QString, password); + + QByteArray plain = readFile(absFilePath); + QSslKey key(plain, algorithm, format, type); + QVERIFY(!key.isNull()); + + QByteArray pwBytes(password.toLatin1()); + + if (type == QSsl::PrivateKey) { + QByteArray encryptedPem = key.toPem(pwBytes); + QVERIFY(!encryptedPem.isEmpty()); + QSslKey keyPem(encryptedPem, algorithm, QSsl::Pem, type, pwBytes); + QVERIFY(!keyPem.isNull()); + QCOMPARE(keyPem, key); + QCOMPARE(keyPem.toPem(), key.toPem()); + } else { + // verify that public keys are never encrypted by toPem() + QByteArray encryptedPem = key.toPem(pwBytes); + QVERIFY(!encryptedPem.isEmpty()); + QByteArray plainPem = key.toPem(); + QVERIFY(!plainPem.isEmpty()); + QCOMPARE(encryptedPem, plainPem); + } + + if (type == QSsl::PrivateKey) { + QByteArray encryptedDer = key.toDer(pwBytes); + // ### at this point, encryptedDer is invalid, hence the below QEXPECT_FAILs + QVERIFY(!encryptedDer.isEmpty()); + QSslKey keyDer(encryptedDer, algorithm, QSsl::Der, type, pwBytes); + if (type == QSsl::PrivateKey) + QEXPECT_FAIL( + QTest::currentDataTag(), "We're not able to decrypt these yet...", Continue); + QVERIFY(!keyDer.isNull()); + if (type == QSsl::PrivateKey) + QEXPECT_FAIL( + QTest::currentDataTag(), "We're not able to decrypt these yet...", Continue); + QCOMPARE(keyDer.toPem(), key.toPem()); + } else { + // verify that public keys are never encrypted by toDer() + QByteArray encryptedDer = key.toDer(pwBytes); + QVERIFY(!encryptedDer.isEmpty()); + QByteArray plainDer = key.toDer(); + QVERIFY(!plainDer.isEmpty()); + QCOMPARE(encryptedDer, plainDer); + } + + // ### add a test to verify that public keys are _decrypted_ correctly (by the ctor) +} + +void tst_QSslKey::passphraseChecks() +{ + { + QString fileName(SRCDIR "/rsa-with-passphrase.pem"); + QFile keyFile(fileName); + QVERIFY(keyFile.exists()); + { + if (!keyFile.isOpen()) + keyFile.open(QIODevice::ReadOnly); + else + keyFile.reset(); + QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey); + QVERIFY(key.isNull()); // null passphrase => should not be able to decode key + } + { + if (!keyFile.isOpen()) + keyFile.open(QIODevice::ReadOnly); + else + keyFile.reset(); + QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, ""); + QVERIFY(key.isNull()); // empty passphrase => should not be able to decode key + } + { + if (!keyFile.isOpen()) + keyFile.open(QIODevice::ReadOnly); + else + keyFile.reset(); + QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, "WRONG!"); + QVERIFY(key.isNull()); // wrong passphrase => should not be able to decode key + } + { + if (!keyFile.isOpen()) + keyFile.open(QIODevice::ReadOnly); + else + keyFile.reset(); + QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, "123"); + QVERIFY(!key.isNull()); // correct passphrase + } + } + + { + // be sure and check a key without passphrase too + QString fileName(SRCDIR "/rsa-without-passphrase.pem"); + QFile keyFile(fileName); + { + if (!keyFile.isOpen()) + keyFile.open(QIODevice::ReadOnly); + else + keyFile.reset(); + QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey); + QVERIFY(!key.isNull()); // null passphrase => should be able to decode key + } + { + if (!keyFile.isOpen()) + keyFile.open(QIODevice::ReadOnly); + else + keyFile.reset(); + QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, ""); + QVERIFY(!key.isNull()); // empty passphrase => should be able to decode key + } + { + if (!keyFile.isOpen()) + keyFile.open(QIODevice::ReadOnly); + else + keyFile.reset(); + QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, "xxx"); + QVERIFY(!key.isNull()); // passphrase given but key is not encrypted anyway => should work + } + } +} + +#endif + +QTEST_MAIN(tst_QSslKey) +#include "tst_qsslkey.moc" diff --git a/tests/auto/network/ssl/qsslsocket/.gitignore b/tests/auto/network/ssl/qsslsocket/.gitignore new file mode 100644 index 0000000000..f2319a97eb --- /dev/null +++ b/tests/auto/network/ssl/qsslsocket/.gitignore @@ -0,0 +1 @@ +tst_qsslsocket diff --git a/tests/auto/network/ssl/qsslsocket/certs/aspiriniks.ca.crt b/tests/auto/network/ssl/qsslsocket/certs/aspiriniks.ca.crt new file mode 100644 index 0000000000..36436b6248 --- /dev/null +++ b/tests/auto/network/ssl/qsslsocket/certs/aspiriniks.ca.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDnDCCAoQCCQDV3otC4hs2KTANBgkqhkiG9w0BAQUFADCBjzELMAkGA1UEBhMC +Tk8xDTALBgNVBAgTBE9zbG8xDTALBgNVBAcTBE9zbG8xDzANBgNVBAoTBlRUIEFT +QTEOMAwGA1UECxMFUVQgU1cxHDAaBgNVBAMTE2FzcGlyaW5pa3MudHJvbGwubm8x +IzAhBgkqhkiG9w0BCQEWFGFiYWJpY0B0cm9sbHRlY2guY29tMB4XDTA4MTEwMTA4 +NTcyOFoXDTA5MTEwMTA4NTcyOFowgY8xCzAJBgNVBAYTAk5PMQ0wCwYDVQQIEwRP +c2xvMQ0wCwYDVQQHEwRPc2xvMQ8wDQYDVQQKEwZUVCBBU0ExDjAMBgNVBAsTBVFU +IFNXMRwwGgYDVQQDExNhc3BpcmluaWtzLnRyb2xsLm5vMSMwIQYJKoZIhvcNAQkB +FhRhYmFiaWNAdHJvbGx0ZWNoLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAMV2bMD1DN3DMgbxU3DXw2i7EWGDXcWjTDtdHvqgIb+9nHqo3MJSrzJy +qgEPoOsXqswMla9wDPZAsWv5gVAmVSqpy2lfEgfY7LaSHiGD75seF7zIy+CxREHW +DofHXpJGGJpBCZEKQt2HfHu3+yAYNPucN78tWNZAcPbUg5tfxMZeepRimAZNIxBI +93SDrl/f9Ka7hvPSzUQsnp8hfdpHlFPFznKfD6yPrjxgz2mT9efavJ4DhtyIa4m+ +paiX515CidDz4A8CFxKZbYvuqq1ilibF/si2so9VhALC77ZcAJP1IMuT8T+WUCxq +skJqiSCncl0Hgr+ba8MDGF9UQYowgjMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA +KcJuNUHvjB8ok3cnTmQEeF0LPPkgj28Tqb5TFB8xpVfRI+wvTYsHsmGdOKCgYJ3a +7VflIsr63ojG8/rXK8H/cx2o2f2Hr3liJdi1UnoLDDRjBqGGz7JNuMreYokPvIbm +eP01mVyK4PO2iYRwHUIAw5eeB1vMWKX2z95MupD+HRLtmGyaLALg8aQxj5N84Ewl +eU2PQfhv8A1wj7aL17kfEUxDerQ1kUzlThJMV1J8Dl0l4C9N8evQkelROJU00i46 +oJikA8BW6EpgbnGyNyyj5Loy4wLPKew9nTS8MCJ5xPMQc0urbY/VzuOeUK7WQof7 +xOFSsRAVyQv+yqgmcZMCtg== +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslsocket/certs/fake-login.live.com.key b/tests/auto/network/ssl/qsslsocket/certs/fake-login.live.com.key new file mode 100644 index 0000000000..692a7bd85d --- /dev/null +++ b/tests/auto/network/ssl/qsslsocket/certs/fake-login.live.com.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDOtxdvMa0VHUQYG5q7Tsi1Jj4qKEJppyZEkmuRXOi0fDbd1SwE +bwHrLGMvDO6OMrYBbq3WDNrtnIfF9CvzUOEch+gjr4hEVQqecU5fb45Wor7yNel3 +/C/gxfbzuXHrsj/gUjNghL2i10+c2NW+hUo/sWO6OusaBT6d6s7ee+YBcQIDAQAB +AoGAb8cVhu0HuLkgjyCuJMbPRRUu3ED02Iin6sB6JhplQuNAD+grayJTmUVhRJnr +jTziqhedLHe7Em1oBaSo92MutfMpXvWiccSlbNygI61VgmrJpVB+qIN5H9cQc9ql +Zymc+nIPa1+i5rsrOzlpUytTh7AsbZ27QG4tQXR/kQejEiECQQD6BgTxBeT8D7x9 +DuukoBaSCkLwx7U7P1NXx15EI3lA1nO51t6UHfvk/jGPp8Sl4wv4alJ7AQxr5uQ/ +vC3kzA/1AkEA06gNu10se8pe3n8qL2RRt+FmVjHkQdD9Mm2Dx9oWCs2A4wOSOrlo +6/nKYF1CaQNYn9HgsNbHVEUpnICVO18qDQJBALEw/uOJ1+TDikPfBSWgxx4s45Ad +GNWqZXh6NNZ5hX9r/IwiOZAjR9fcRmeW8IjYRi2BvH6sGY+HDRAWXzgdXtkCQCma +dOiJTf8fLjqp4E7kdzOfuI/kyqstOze4Uxjrgz2oW1dEEnA8laUcumzqp+0gXUE8 +7d+UuCWWWrGKjMrYz9kCQQDh5E5+b6Djn082Jo6gvyuXWC5eXju6IdmihlJ2SMzD +s2y3IDjOUtTeQQRDymLneteMz0ha79KeUp6VnAvZCOVe +-----END RSA PRIVATE KEY----- diff --git a/tests/auto/network/ssl/qsslsocket/certs/fake-login.live.com.pem b/tests/auto/network/ssl/qsslsocket/certs/fake-login.live.com.pem new file mode 100644 index 0000000000..429f95187c --- /dev/null +++ b/tests/auto/network/ssl/qsslsocket/certs/fake-login.live.com.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDDjCCAnegAwIBAgIRALC3Ez7Qlvm1b66RyHS9OsAwDQYJKoZIhvcNAQEFBQAw +XjELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGElu +dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEXMBUGA1UEAxMObG9naW4ubGl2ZS5jb20w +HhcNMTEwMzI1MTMyODUwWhcNMTEwNDI0MTMyODUwWjBeMQswCQYDVQQGEwJBVTET +MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ +dHkgTHRkMRcwFQYDVQQDEw5sb2dpbi5saXZlLmNvbTCBnzANBgkqhkiG9w0BAQEF +AAOBjQAwgYkCgYEAzrcXbzGtFR1EGBuau07ItSY+KihCaacmRJJrkVzotHw23dUs +BG8B6yxjLwzujjK2AW6t1gza7ZyHxfQr81DhHIfoI6+IRFUKnnFOX2+OVqK+8jXp +d/wv4MX287lx67I/4FIzYIS9otdPnNjVvoVKP7FjujrrGgU+nerO3nvmAXECAwEA +AaOByzCByDAdBgNVHQ4EFgQUpSOEcmtkQITvBdM2IDfcXnJ0FCAwgZgGA1UdIwSB +kDCBjYAUpSOEcmtkQITvBdM2IDfcXnJ0FCChYqRgMF4xCzAJBgNVBAYTAkFVMRMw +EQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0 +eSBMdGQxFzAVBgNVBAMTDmxvZ2luLmxpdmUuY29tghEAsLcTPtCW+bVvrpHIdL06 +wDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAD+2HT4GSHHKCdbl9VkX +zsl+D+drMm2b0ksxz9SgPihP7aW50EEIJDEEihNMTa27mhpeOXHc/sLqDi4ECUao +/0Ns/5uoVuAIrAKCydmtPsonVFh9XWjyrfUzPOHAc9p2bmJ1i9a3kTsLB6jlrVDO +VufGzsowHlHZ0TtKf5omojU5 +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslsocket/certs/fluke.cert b/tests/auto/network/ssl/qsslsocket/certs/fluke.cert new file mode 100644 index 0000000000..069fa6b341 --- /dev/null +++ b/tests/auto/network/ssl/qsslsocket/certs/fluke.cert @@ -0,0 +1,75 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 0 (0x0) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=NO, ST=Oslo, L=Nydalen, O=Nokia Corporation and/or its subsidiary(-ies), OU=Development, CN=fluke.troll.no/emailAddress=ahanssen@trolltech.com + Validity + Not Before: Dec 4 01:10:32 2007 GMT + Not After : Apr 21 01:10:32 2035 GMT + Subject: C=NO, ST=Oslo, O=Nokia Corporation and/or its subsidiary(-ies), OU=Development, CN=fluke.troll.no + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (1024 bit) + Modulus (1024 bit): + 00:a7:c8:a0:4a:c4:19:05:1b:66:ba:32:e2:d2:f1: + 1c:6f:17:82:e4:39:2e:01:51:90:db:04:34:32:11: + 21:c2:0d:6f:59:d8:53:90:54:3f:83:8f:a9:d3:b3: + d5:ee:1a:9b:80:ae:c3:25:c9:5e:a5:af:4b:60:05: + aa:a0:d1:91:01:1f:ca:04:83:e3:58:1c:99:32:45: + 84:70:72:58:03:98:4a:63:8b:41:f5:08:49:d2:91: + 02:60:6b:e4:64:fe:dd:a0:aa:74:08:e9:34:4c:91: + 5f:12:3d:37:4d:54:2c:ad:7f:5b:98:60:36:02:8c: + 3b:f6:45:f3:27:6a:9b:94:9d + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Netscape Comment: + OpenSSL Generated Certificate + X509v3 Subject Key Identifier: + 21:85:04:3D:23:01:66:E5:F7:9F:1A:84:24:8A:AF:0A:79:F4:E5:AC + X509v3 Authority Key Identifier: + DirName:/C=NO/ST=Oslo/L=Nydalen/O=Nokia Corporation and/or its subsidiary(-ies)/OU=Development/CN=fluke.troll.no/emailAddress=ahanssen@trolltech.com + serial:8E:A8:B4:E8:91:B7:54:2E + + Signature Algorithm: sha1WithRSAEncryption + 6d:57:5f:d1:05:43:f0:62:05:ec:2a:71:a5:dc:19:08:f2:c4: + a6:bd:bb:25:d9:ca:89:01:0e:e4:cf:1f:c1:8c:c8:24:18:35: + 53:59:7b:c0:43:b4:32:e6:98:b2:a6:ef:15:05:0b:48:5f:e1: + a0:0c:97:a9:a1:77:d8:35:18:30:bc:a9:8f:d3:b7:54:c7:f1: + a9:9e:5d:e6:19:bf:f6:3c:5b:2b:d8:e4:3e:62:18:88:8b:d3: + 24:e1:40:9b:0c:e6:29:16:62:ab:ea:05:24:70:36:aa:55:93: + ef:02:81:1b:23:10:a2:04:eb:56:95:75:fc:f8:94:b1:5d:42: + c5:3f:36:44:85:5d:3a:2e:90:46:8a:a2:b9:6f:87:ae:0c:15: + 40:19:31:90:fc:3b:25:bb:ae:f1:66:13:0d:85:90:d9:49:34: + 8f:f2:5d:f9:7a:db:4d:5d:27:f6:76:9d:35:8c:06:a6:4c:a3: + b1:b2:b6:6f:1d:d7:a3:00:fd:72:eb:9e:ea:44:a1:af:21:34: + 7d:c7:42:e2:49:91:19:8b:c0:ad:ba:82:80:a8:71:70:f4:35: + 31:91:63:84:20:95:e9:60:af:64:8b:cc:ff:3d:8a:76:74:3d: + c8:55:6d:e4:8e:c3:2b:1c:e8:42:18:ae:9f:e6:6b:9c:34:06: + ec:6a:f2:c3 +-----BEGIN CERTIFICATE----- +MIIEEzCCAvugAwIBAgIBADANBgkqhkiG9w0BAQUFADCBnDELMAkGA1UEBhMCTk8x +DTALBgNVBAgTBE9zbG8xEDAOBgNVBAcTB055ZGFsZW4xFjAUBgNVBAoTDVRyb2xs +dGVjaCBBU0ExFDASBgNVBAsTC0RldmVsb3BtZW50MRcwFQYDVQQDEw5mbHVrZS50 +cm9sbC5ubzElMCMGCSqGSIb3DQEJARYWYWhhbnNzZW5AdHJvbGx0ZWNoLmNvbTAe +Fw0wNzEyMDQwMTEwMzJaFw0zNTA0MjEwMTEwMzJaMGMxCzAJBgNVBAYTAk5PMQ0w +CwYDVQQIEwRPc2xvMRYwFAYDVQQKEw1Ucm9sbHRlY2ggQVNBMRQwEgYDVQQLEwtE +ZXZlbG9wbWVudDEXMBUGA1UEAxMOZmx1a2UudHJvbGwubm8wgZ8wDQYJKoZIhvcN +AQEBBQADgY0AMIGJAoGBAKfIoErEGQUbZroy4tLxHG8XguQ5LgFRkNsENDIRIcIN +b1nYU5BUP4OPqdOz1e4am4CuwyXJXqWvS2AFqqDRkQEfygSD41gcmTJFhHByWAOY +SmOLQfUISdKRAmBr5GT+3aCqdAjpNEyRXxI9N01ULK1/W5hgNgKMO/ZF8ydqm5Sd +AgMBAAGjggEaMIIBFjAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NM +IEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUIYUEPSMBZuX3nxqEJIqv +Cnn05awwgbsGA1UdIwSBszCBsKGBoqSBnzCBnDELMAkGA1UEBhMCTk8xDTALBgNV +BAgTBE9zbG8xEDAOBgNVBAcTB055ZGFsZW4xFjAUBgNVBAoTDVRyb2xsdGVjaCBB +U0ExFDASBgNVBAsTC0RldmVsb3BtZW50MRcwFQYDVQQDEw5mbHVrZS50cm9sbC5u +bzElMCMGCSqGSIb3DQEJARYWYWhhbnNzZW5AdHJvbGx0ZWNoLmNvbYIJAI6otOiR +t1QuMA0GCSqGSIb3DQEBBQUAA4IBAQBtV1/RBUPwYgXsKnGl3BkI8sSmvbsl2cqJ +AQ7kzx/BjMgkGDVTWXvAQ7Qy5piypu8VBQtIX+GgDJepoXfYNRgwvKmP07dUx/Gp +nl3mGb/2PFsr2OQ+YhiIi9Mk4UCbDOYpFmKr6gUkcDaqVZPvAoEbIxCiBOtWlXX8 ++JSxXULFPzZEhV06LpBGiqK5b4euDBVAGTGQ/Dslu67xZhMNhZDZSTSP8l35ettN +XSf2dp01jAamTKOxsrZvHdejAP1y657qRKGvITR9x0LiSZEZi8CtuoKAqHFw9DUx +kWOEIJXpYK9ki8z/PYp2dD3IVW3kjsMrHOhCGK6f5mucNAbsavLD +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslsocket/certs/fluke.key b/tests/auto/network/ssl/qsslsocket/certs/fluke.key new file mode 100644 index 0000000000..9d1664d609 --- /dev/null +++ b/tests/auto/network/ssl/qsslsocket/certs/fluke.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQCnyKBKxBkFG2a6MuLS8RxvF4LkOS4BUZDbBDQyESHCDW9Z2FOQ +VD+Dj6nTs9XuGpuArsMlyV6lr0tgBaqg0ZEBH8oEg+NYHJkyRYRwclgDmEpji0H1 +CEnSkQJga+Rk/t2gqnQI6TRMkV8SPTdNVCytf1uYYDYCjDv2RfMnapuUnQIDAQAB +AoGANFzLkanTeSGNFM0uttBipFT9F4a00dqHz6JnO7zXAT26I5r8sU1pqQBb6uLz +/+Qz5Zwk8RUAQcsMRgJetuPQUb0JZjF6Duv24hNazqXBCu7AZzUenjafwmKC/8ri +KpX3fTwqzfzi//FKGgbXQ80yykSSliDL3kn/drATxsLCgQECQQDXhEFWLJ0vVZ1s +1Ekf+3NITE+DR16X+LQ4W6vyEHAjTbaNWtcTKdAWLA2l6N4WAAPYSi6awm+zMxx4 +VomVTsjdAkEAx0z+e7natLeFcrrq8pbU+wa6SAP1VfhQWKitxL1e7u/QO90NCpxE +oQYKzMkmmpOOFjQwEMAy1dvFMbm4LHlewQJAC/ksDBaUcQHHqjktCtrUb8rVjAyW +A8lscckeB2fEYyG5J6dJVaY4ClNOOs5yMDS2Afk1F6H/xKvtQ/5CzInA/QJATDub +K+BPU8jO9q+gpuIi3VIZdupssVGmCgObVCHLakG4uO04y9IyPhV9lA9tALtoIf4c +VIvv5fWGXBrZ48kZAQJBAJmVCdzQxd9LZI5vxijUCj5EI4e+x5DRqVUvyP8KCZrC +AiNyoDP85T+hBZaSXK3aYGpVwelyj3bvo1GrTNwNWLw= +-----END RSA PRIVATE KEY----- diff --git a/tests/auto/network/ssl/qsslsocket/certs/qt-test-server-cacert.pem b/tests/auto/network/ssl/qsslsocket/certs/qt-test-server-cacert.pem new file mode 100644 index 0000000000..25bd4046e8 --- /dev/null +++ b/tests/auto/network/ssl/qsslsocket/certs/qt-test-server-cacert.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICrTCCAhYCCQCdDn5rci6VDjANBgkqhkiG9w0BAQQFADCBmjEOMAwGA1UEChMF +Tm9raWExFDASBgNVBAsTC1F0IFNvZnR3YXJlMSIwIAYJKoZIhvcNAQkBFhNub2Jv +ZHlAbm9kb21haW4ub3JnMQ0wCwYDVQQHEwRPc2xvMQ0wCwYDVQQIEwRPc2xvMQsw +CQYDVQQGEwJOTzEjMCEGA1UEAxMacXQtdGVzdC1zZXJ2ZXIucXQtdGVzdC1uZXQw +HhcNMDkwNzEwMDc0MTIzWhcNMTkwNzA4MDc0MTIzWjCBmjEOMAwGA1UEChMFTm9r +aWExFDASBgNVBAsTC1F0IFNvZnR3YXJlMSIwIAYJKoZIhvcNAQkBFhNub2JvZHlA +bm9kb21haW4ub3JnMQ0wCwYDVQQHEwRPc2xvMQ0wCwYDVQQIEwRPc2xvMQswCQYD +VQQGEwJOTzEjMCEGA1UEAxMacXQtdGVzdC1zZXJ2ZXIucXQtdGVzdC1uZXQwgZ8w +DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM2q22/WNMmn8cC+5EEYGeICySLmp9W6 +Ay6eKHr0Xxp3X3epETuPfvAuxp7rOtkS18EMUegkUj8jw0IMEcbyHKFC/rTCaYOt +93CxGBXMIChiMPAsFeYzGa/D6xzAkfcRaJRQ+Ek3CDLXPnXfo7xpABXezYcPXAJr +gsgBfWrwHdxzAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEAy7YOLCZABQy2Ygkchq1I ++TUpvMn+gLwAyW8TNErM1V4lNY2+K78RawzKx3SqM97ymCy4TD45EA3A2gmi32NI +xSKBNjFyzngUqsXBdcSasALiowlZCiJrGwlGX5qCkBlxXvJeUEbuJLPYVl5FBjXZ +6o00K4cSPCqtqUez7WSmDZU= +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslsocket/qsslsocket.pro b/tests/auto/network/ssl/qsslsocket/qsslsocket.pro new file mode 100644 index 0000000000..1f5c7f6aaf --- /dev/null +++ b/tests/auto/network/ssl/qsslsocket/qsslsocket.pro @@ -0,0 +1,48 @@ +load(qttest_p4) + +SOURCES += tst_qsslsocket.cpp +!wince*:win32:LIBS += -lws2_32 +QT += core-private network-private +QT -= gui + +TARGET = tst_qsslsocket + +win32 { + CONFIG(debug, debug|release) { + DESTDIR = debug +} else { + DESTDIR = release + } +} + +# OpenSSL support +contains(QT_CONFIG, openssl) | contains(QT_CONFIG, openssl-linked) { + symbian { + INCLUDEPATH *= $$OS_LAYER_SSL_SYSTEMINCLUDE + } else { + include($$QT_SOURCE_TREE/config.tests/unix/openssl/openssl.pri) + } + # Add optional SSL libs + LIBS += $$OPENSSL_LIBS +} + +wince* { + DEFINES += SRCDIR=\\\"./\\\" + + certFiles.files = certs ssl.tar.gz + certFiles.path = . + DEPLOYMENT += certFiles +} else:symbian { + DEFINES += QSSLSOCKET_CERTUNTRUSTED_WORKAROUND + TARGET.EPOCHEAPSIZE="0x100 0x3000000" + TARGET.CAPABILITY=NetworkServices ReadUserData + + certFiles.files = certs ssl.tar.gz + certFiles.path = . + DEPLOYMENT += certFiles + INCLUDEPATH *= $$MW_LAYER_SYSTEMINCLUDE # Needed for e32svr.h in S^3 envs +} else { + DEFINES += SRCDIR=\\\"$$PWD/\\\" +} + +requires(contains(QT_CONFIG,private_tests)) diff --git a/tests/auto/network/ssl/qsslsocket/ssl.tar.gz b/tests/auto/network/ssl/qsslsocket/ssl.tar.gz new file mode 100644 index 0000000000..b59af51f46 Binary files /dev/null and b/tests/auto/network/ssl/qsslsocket/ssl.tar.gz differ diff --git a/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp new file mode 100644 index 0000000000..6b7720308d --- /dev/null +++ b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp @@ -0,0 +1,2097 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "private/qhostinfo_p.h" +#ifndef QT_NO_OPENSSL +#include "private/qsslsocket_openssl_p.h" +#include "private/qsslsocket_openssl_symbols_p.h" +#endif + +#include "../../../network-settings.h" + +Q_DECLARE_METATYPE(QAbstractSocket::SocketState) +Q_DECLARE_METATYPE(QAbstractSocket::SocketError) +#ifndef QT_NO_OPENSSL +Q_DECLARE_METATYPE(QSslSocket::SslMode) +typedef QList SslErrorList; +Q_DECLARE_METATYPE(SslErrorList) +Q_DECLARE_METATYPE(QSslError) +Q_DECLARE_METATYPE(QSsl::SslProtocol) +Q_DECLARE_METATYPE(QSslConfiguration) +#endif + +#if defined Q_OS_HPUX && defined Q_CC_GNU +// This error is delivered every time we try to use the fluke CA +// certificate. For now we work around this bug. Task 202317. +#define QSSLSOCKET_CERTUNTRUSTED_WORKAROUND +#endif + +#ifdef Q_OS_SYMBIAN +#define SRCDIR "" +#endif + +#ifndef QT_NO_OPENSSL +class QSslSocketPtr: public QSharedPointer +{ +public: + inline QSslSocketPtr(QSslSocket *ptr = 0) + : QSharedPointer(ptr) + { } + + inline operator QSslSocket *() const { return data(); } +}; +#endif + +class tst_QSslSocket : public QObject +{ + Q_OBJECT + + int proxyAuthCalled; + +public: + tst_QSslSocket(); + virtual ~tst_QSslSocket(); + + static void enterLoop(int secs) + { + ++loopLevel; + QTestEventLoop::instance().enterLoop(secs); + } + + static bool timeout() + { + return QTestEventLoop::instance().timeout(); + } + +#ifndef QT_NO_OPENSSL + QSslSocketPtr newSocket(); +#endif + +public slots: + void initTestCase_data(); + void init(); + void cleanup(); + void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth); + +#ifndef QT_NO_OPENSSL +private slots: + void constructing(); + void simpleConnect(); + void simpleConnectWithIgnore(); + + // API tests + void sslErrors_data(); + void sslErrors(); + void addCaCertificate(); + void addCaCertificates(); + void addCaCertificates2(); + void ciphers(); + void connectToHostEncrypted(); + void connectToHostEncryptedWithVerificationPeerName(); + void sessionCipher(); + void flush(); + void isEncrypted(); + void localCertificate(); + void mode(); + void peerCertificate(); + void peerCertificateChain(); + void privateKey(); + void privateKeyOpaque(); + void protocol(); + void protocolServerSide_data(); + void protocolServerSide(); + void setCaCertificates(); + void setLocalCertificate(); + void setPrivateKey(); + void setSocketDescriptor(); + void setSslConfiguration_data(); + void setSslConfiguration(); + void waitForEncrypted(); + void waitForEncryptedMinusOne(); + void waitForConnectedEncryptedReadyRead(); + void startClientEncryption(); + void startServerEncryption(); + void addDefaultCaCertificate(); + void addDefaultCaCertificates(); + void addDefaultCaCertificates2(); + void defaultCaCertificates(); + void defaultCiphers(); + void resetDefaultCiphers(); + void setDefaultCaCertificates(); + void setDefaultCiphers(); + void supportedCiphers(); + void systemCaCertificates(); + void wildcardCertificateNames(); + void wildcard(); + void setEmptyKey(); + void spontaneousWrite(); + void setReadBufferSize(); + void setReadBufferSize_task_250027(); + void waitForMinusOne(); + void verifyMode(); + void verifyDepth(); + void peerVerifyError(); + void disconnectFromHostWhenConnecting(); + void disconnectFromHostWhenConnected(); + void resetProxy(); + void ignoreSslErrorsList_data(); + void ignoreSslErrorsList(); + void ignoreSslErrorsListWithSlot_data(); + void ignoreSslErrorsListWithSlot(); + void readFromClosedSocket(); + void writeBigChunk(); + void blacklistedCertificates(); + void setEmptyDefaultConfiguration(); + void versionAccessors(); + + static void exitLoop() + { + // Safe exit - if we aren't in an event loop, don't + // exit one. + if (loopLevel > 0) { + --loopLevel; + QTestEventLoop::instance().exitLoop(); + } + } + +protected slots: + void ignoreErrorSlot() + { + socket->ignoreSslErrors(); + } + void untrustedWorkaroundSlot(const QList &errors) + { + if (errors.size() == 1 && + (errors.first().error() == QSslError::CertificateUntrusted || + errors.first().error() == QSslError::SelfSignedCertificate)) + socket->ignoreSslErrors(); + } + void ignoreErrorListSlot(const QList &errors); + +private: + QSslSocket *socket; + QList storedExpectedSslErrors; +#endif // QT_NO_OPENSSL +private: + static int loopLevel; +}; + +int tst_QSslSocket::loopLevel = 0; + +tst_QSslSocket::tst_QSslSocket() +{ +#ifndef QT_NO_OPENSSL + qRegisterMetaType >("QList"); + qRegisterMetaType("QSslError"); + qRegisterMetaType("QAbstractSocket::SocketState"); + qRegisterMetaType("QAbstractSocket::SocketError"); + qRegisterMetaType("QSslSocket::SslMode"); +#endif + Q_SET_DEFAULT_IAP +} + +tst_QSslSocket::~tst_QSslSocket() +{ +} + +enum ProxyTests { + NoProxy = 0x00, + Socks5Proxy = 0x01, + HttpProxy = 0x02, + TypeMask = 0x0f, + + NoAuth = 0x00, + AuthBasic = 0x10, + AuthNtlm = 0x20, + AuthMask = 0xf0 +}; + +void tst_QSslSocket::initTestCase_data() +{ + QTest::addColumn("setProxy"); + QTest::addColumn("proxyType"); + + QTest::newRow("WithoutProxy") << false << 0; + QTest::newRow("WithSocks5Proxy") << true << int(Socks5Proxy); + QTest::newRow("WithSocks5ProxyAuth") << true << int(Socks5Proxy | AuthBasic); + + QTest::newRow("WithHttpProxy") << true << int(HttpProxy); + QTest::newRow("WithHttpProxyBasicAuth") << true << int(HttpProxy | AuthBasic); + // uncomment the line below when NTLM works +// QTest::newRow("WithHttpProxyNtlmAuth") << true << int(HttpProxy | AuthNtlm); +} + +void tst_QSslSocket::init() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) { + QFETCH_GLOBAL(int, proxyType); + QString fluke = QHostInfo::fromName(QtNetworkSettings::serverName()).addresses().first().toString(); + QNetworkProxy proxy; + + switch (proxyType) { + case Socks5Proxy: + proxy = QNetworkProxy(QNetworkProxy::Socks5Proxy, fluke, 1080); + break; + + case Socks5Proxy | AuthBasic: + proxy = QNetworkProxy(QNetworkProxy::Socks5Proxy, fluke, 1081); + break; + + case HttpProxy | NoAuth: + proxy = QNetworkProxy(QNetworkProxy::HttpProxy, fluke, 3128); + break; + + case HttpProxy | AuthBasic: + proxy = QNetworkProxy(QNetworkProxy::HttpProxy, fluke, 3129); + break; + + case HttpProxy | AuthNtlm: + proxy = QNetworkProxy(QNetworkProxy::HttpProxy, fluke, 3130); + break; + } + QNetworkProxy::setApplicationProxy(proxy); + } + + qt_qhostinfo_clear_cache(); +} + +void tst_QSslSocket::cleanup() +{ + QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy); +} + +#ifndef QT_NO_OPENSSL +QSslSocketPtr tst_QSslSocket::newSocket() +{ + QSslSocket *socket = new QSslSocket; + + proxyAuthCalled = 0; + connect(socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + Qt::DirectConnection); + + return QSslSocketPtr(socket); +} +#endif + +void tst_QSslSocket::proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth) +{ + ++proxyAuthCalled; + auth->setUser("qsockstest"); + auth->setPassword("password"); +} + +#ifndef QT_NO_OPENSSL + +void tst_QSslSocket::constructing() +{ + if (!QSslSocket::supportsSsl()) + return; + + QSslSocket socket; + + QCOMPARE(socket.state(), QSslSocket::UnconnectedState); + QCOMPARE(socket.mode(), QSslSocket::UnencryptedMode); + QVERIFY(!socket.isEncrypted()); + QCOMPARE(socket.bytesAvailable(), qint64(0)); + QCOMPARE(socket.bytesToWrite(), qint64(0)); + QVERIFY(!socket.canReadLine()); + QVERIFY(socket.atEnd()); + QCOMPARE(socket.localCertificate(), QSslCertificate()); + QCOMPARE(socket.sslConfiguration(), QSslConfiguration::defaultConfiguration()); + QCOMPARE(socket.errorString(), QString("Unknown error")); + char c = '\0'; + QVERIFY(!socket.getChar(&c)); + QCOMPARE(c, '\0'); + QVERIFY(!socket.isOpen()); + QVERIFY(!socket.isReadable()); + QVERIFY(socket.isSequential()); + QVERIFY(!socket.isTextModeEnabled()); + QVERIFY(!socket.isWritable()); + QCOMPARE(socket.openMode(), QIODevice::NotOpen); + QVERIFY(socket.peek(2).isEmpty()); + QCOMPARE(socket.pos(), qint64(0)); + QVERIFY(!socket.putChar('c')); + QVERIFY(socket.read(2).isEmpty()); + QCOMPARE(socket.read(0, 0), qint64(-1)); + QVERIFY(socket.readAll().isEmpty()); + QTest::ignoreMessage(QtWarningMsg, "QIODevice::readLine: Called with maxSize < 2"); + QCOMPARE(socket.readLine(0, 0), qint64(-1)); + char buf[10]; + QCOMPARE(socket.readLine(buf, sizeof(buf)), qint64(-1)); + QTest::ignoreMessage(QtWarningMsg, "QIODevice::seek: The device is not open"); + QVERIFY(!socket.reset()); + QTest::ignoreMessage(QtWarningMsg, "QIODevice::seek: The device is not open"); + QVERIFY(!socket.seek(2)); + QCOMPARE(socket.size(), qint64(0)); + QVERIFY(!socket.waitForBytesWritten(10)); + QVERIFY(!socket.waitForReadyRead(10)); + QCOMPARE(socket.write(0, 0), qint64(-1)); + QCOMPARE(socket.write(QByteArray()), qint64(-1)); + QCOMPARE(socket.error(), QAbstractSocket::UnknownSocketError); + QVERIFY(!socket.flush()); + QVERIFY(!socket.isValid()); + QCOMPARE(socket.localAddress(), QHostAddress()); + QCOMPARE(socket.localPort(), quint16(0)); + QCOMPARE(socket.peerAddress(), QHostAddress()); + QVERIFY(socket.peerName().isEmpty()); + QCOMPARE(socket.peerPort(), quint16(0)); + QCOMPARE(socket.proxy().type(), QNetworkProxy::DefaultProxy); + QCOMPARE(socket.readBufferSize(), qint64(0)); + QCOMPARE(socket.socketDescriptor(), -1); + QCOMPARE(socket.socketType(), QAbstractSocket::TcpSocket); + QVERIFY(!socket.waitForConnected(10)); + QTest::ignoreMessage(QtWarningMsg, "QSslSocket::waitForDisconnected() is not allowed in UnconnectedState"); + QVERIFY(!socket.waitForDisconnected(10)); + QCOMPARE(socket.protocol(), QSsl::SecureProtocols); + + QSslConfiguration savedDefault = QSslConfiguration::defaultConfiguration(); + + // verify that changing the default config doesn't affect this socket + // (on Unix, the ca certs might be empty, depending on whether we load + // them on demand or not, so set them explicitly) + socket.setCaCertificates(QSslSocket::systemCaCertificates()); + QSslSocket::setDefaultCaCertificates(QList()); + QSslSocket::setDefaultCiphers(QList()); + QVERIFY(!socket.caCertificates().isEmpty()); + QVERIFY(!socket.ciphers().isEmpty()); + + // verify the default as well: + QVERIFY(QSslConfiguration::defaultConfiguration().caCertificates().isEmpty()); + QVERIFY(QSslConfiguration::defaultConfiguration().ciphers().isEmpty()); + + QSslConfiguration::setDefaultConfiguration(savedDefault); +} + +void tst_QSslSocket::simpleConnect() +{ + if (!QSslSocket::supportsSsl()) + return; + + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + QSslSocket socket; + QSignalSpy connectedSpy(&socket, SIGNAL(connected())); + QSignalSpy hostFoundSpy(&socket, SIGNAL(hostFound())); + QSignalSpy disconnectedSpy(&socket, SIGNAL(disconnected())); + QSignalSpy connectionEncryptedSpy(&socket, SIGNAL(encrypted())); + QSignalSpy sslErrorsSpy(&socket, SIGNAL(sslErrors(const QList &))); + + connect(&socket, SIGNAL(connected()), this, SLOT(exitLoop())); + connect(&socket, SIGNAL(disconnected()), this, SLOT(exitLoop())); + connect(&socket, SIGNAL(modeChanged(QSslSocket::SslMode)), this, SLOT(exitLoop())); + connect(&socket, SIGNAL(encrypted()), this, SLOT(exitLoop())); + connect(&socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(exitLoop())); + connect(&socket, SIGNAL(sslErrors(const QList &)), this, SLOT(exitLoop())); + + // Start connecting + socket.connectToHost(QtNetworkSettings::serverName(), 993); + QCOMPARE(socket.state(), QAbstractSocket::HostLookupState); + enterLoop(10); + + // Entered connecting state +#ifndef Q_OS_SYMBIAN + QCOMPARE(socket.state(), QAbstractSocket::ConnectingState); + QCOMPARE(connectedSpy.count(), 0); +#endif + QCOMPARE(hostFoundSpy.count(), 1); + QCOMPARE(disconnectedSpy.count(), 0); + enterLoop(10); + + // Entered connected state + QCOMPARE(socket.state(), QAbstractSocket::ConnectedState); + QCOMPARE(socket.mode(), QSslSocket::UnencryptedMode); + QVERIFY(!socket.isEncrypted()); + QCOMPARE(connectedSpy.count(), 1); + QCOMPARE(hostFoundSpy.count(), 1); + QCOMPARE(disconnectedSpy.count(), 0); + + // Enter encrypted mode + socket.startClientEncryption(); + QCOMPARE(socket.mode(), QSslSocket::SslClientMode); + QVERIFY(!socket.isEncrypted()); + QCOMPARE(connectionEncryptedSpy.count(), 0); + QCOMPARE(sslErrorsSpy.count(), 0); + + // Starting handshake + enterLoop(10); + QCOMPARE(sslErrorsSpy.count(), 1); + QCOMPARE(connectionEncryptedSpy.count(), 0); + QVERIFY(!socket.isEncrypted()); + QCOMPARE(socket.state(), QAbstractSocket::UnconnectedState); +} + +void tst_QSslSocket::simpleConnectWithIgnore() +{ + if (!QSslSocket::supportsSsl()) + return; + + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + QSslSocket socket; + this->socket = &socket; + QSignalSpy encryptedSpy(&socket, SIGNAL(encrypted())); + QSignalSpy sslErrorsSpy(&socket, SIGNAL(sslErrors(const QList &))); + + connect(&socket, SIGNAL(readyRead()), this, SLOT(exitLoop())); + connect(&socket, SIGNAL(encrypted()), this, SLOT(exitLoop())); + connect(&socket, SIGNAL(connected()), this, SLOT(exitLoop())); + connect(&socket, SIGNAL(sslErrors(const QList &)), this, SLOT(ignoreErrorSlot())); + connect(&socket, SIGNAL(sslErrors(const QList &)), this, SLOT(exitLoop())); + + // Start connecting + socket.connectToHost(QtNetworkSettings::serverName(), 993); + QVERIFY(socket.state() != QAbstractSocket::UnconnectedState); // something must be in progress + enterLoop(10); + + // Start handshake + QCOMPARE(socket.state(), QAbstractSocket::ConnectedState); + socket.startClientEncryption(); + enterLoop(10); + + // Done; encryption should be enabled. + QCOMPARE(sslErrorsSpy.count(), 1); + QVERIFY(socket.isEncrypted()); + QCOMPARE(socket.state(), QAbstractSocket::ConnectedState); + QCOMPARE(encryptedSpy.count(), 1); + + // Wait for incoming data + if (!socket.canReadLine()) + enterLoop(10); + + QByteArray data = socket.readAll(); + socket.disconnectFromHost(); + QVERIFY2(QtNetworkSettings::compareReplyIMAPSSL(data), data.constData()); +} + +void tst_QSslSocket::sslErrors_data() +{ + QTest::addColumn("host"); + QTest::addColumn("port"); + QTest::addColumn("expected"); + + QTest::newRow(qPrintable(QtNetworkSettings::serverLocalName())) + << QtNetworkSettings::serverLocalName() + << 993 + << (SslErrorList() << QSslError::HostNameMismatch + << QSslError::SelfSignedCertificate); +} + +void tst_QSslSocket::sslErrors() +{ + QFETCH(QString, host); + QFETCH(int, port); + QFETCH(SslErrorList, expected); + + QSslSocketPtr socket = newSocket(); + socket->connectToHostEncrypted(host, port); + if (!socket->waitForConnected()) + QEXPECT_FAIL("imap.trolltech.com", "server not open to internet", Continue); + socket->waitForEncrypted(5000); + + SslErrorList output; + foreach (QSslError error, socket->sslErrors()) { + output << error.error(); + } + +#ifdef QSSLSOCKET_CERTUNTRUSTED_WORKAROUND + if (output.count() && output.last() == QSslError::CertificateUntrusted) + output.takeLast(); +#endif + QCOMPARE(output, expected); +} + +void tst_QSslSocket::addCaCertificate() +{ + if (!QSslSocket::supportsSsl()) + return; +} + +void tst_QSslSocket::addCaCertificates() +{ + if (!QSslSocket::supportsSsl()) + return; +} + +void tst_QSslSocket::addCaCertificates2() +{ + if (!QSslSocket::supportsSsl()) + return; +} + +void tst_QSslSocket::ciphers() +{ + if (!QSslSocket::supportsSsl()) + return; + + QSslSocket socket; + QCOMPARE(socket.ciphers(), QSslSocket::supportedCiphers()); + socket.setCiphers(QList()); + QVERIFY(socket.ciphers().isEmpty()); + socket.setCiphers(socket.defaultCiphers()); + QCOMPARE(socket.ciphers(), QSslSocket::supportedCiphers()); + socket.setCiphers(socket.defaultCiphers()); + QCOMPARE(socket.ciphers(), QSslSocket::supportedCiphers()); + + // Task 164356 + socket.setCiphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"); +} + +void tst_QSslSocket::connectToHostEncrypted() +{ + if (!QSslSocket::supportsSsl()) + return; + + QSslSocketPtr socket = newSocket(); + this->socket = socket; + QVERIFY(socket->addCaCertificates(QLatin1String(SRCDIR "certs/qt-test-server-cacert.pem"))); +#ifdef QSSLSOCKET_CERTUNTRUSTED_WORKAROUND + connect(socket, SIGNAL(sslErrors(QList)), + this, SLOT(untrustedWorkaroundSlot(QList))); +#endif + + socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443); + + // This should pass unconditionally when using fluke's CA certificate. + // or use untrusted certificate workaround + QVERIFY2(socket->waitForEncrypted(10000), qPrintable(socket->errorString())); + + socket->disconnectFromHost(); + QVERIFY(socket->waitForDisconnected()); + + QCOMPARE(socket->mode(), QSslSocket::SslClientMode); + + socket->connectToHost(QtNetworkSettings::serverName(), 13); + + QCOMPARE(socket->mode(), QSslSocket::UnencryptedMode); + + QVERIFY(socket->waitForDisconnected()); +} + +void tst_QSslSocket::connectToHostEncryptedWithVerificationPeerName() +{ + if (!QSslSocket::supportsSsl()) + return; + + QSslSocketPtr socket = newSocket(); + this->socket = socket; + + socket->addCaCertificates(QLatin1String(SRCDIR "certs/qt-test-server-cacert.pem")); +#ifdef QSSLSOCKET_CERTUNTRUSTED_WORKAROUND + connect(socket, SIGNAL(sslErrors(QList)), + this, SLOT(untrustedWorkaroundSlot(QList))); +#endif + + // connect to the server with its local name, but use the full name for verification. + socket->connectToHostEncrypted(QtNetworkSettings::serverLocalName(), 443, QtNetworkSettings::serverName()); + + // This should pass unconditionally when using fluke's CA certificate. + QVERIFY2(socket->waitForEncrypted(10000), qPrintable(socket->errorString())); + + socket->disconnectFromHost(); + QVERIFY(socket->waitForDisconnected()); + + QCOMPARE(socket->mode(), QSslSocket::SslClientMode); +} + +void tst_QSslSocket::sessionCipher() +{ + if (!QSslSocket::supportsSsl()) + return; + + QSslSocketPtr socket = newSocket(); + this->socket = socket; + connect(socket, SIGNAL(sslErrors(QList)), this, SLOT(ignoreErrorSlot())); + QVERIFY(socket->sessionCipher().isNull()); + socket->connectToHost(QtNetworkSettings::serverName(), 443 /* https */); + QVERIFY(socket->waitForConnected(10000)); + QVERIFY(socket->sessionCipher().isNull()); + socket->startClientEncryption(); + QVERIFY(socket->waitForEncrypted(5000)); + QVERIFY(!socket->sessionCipher().isNull()); + QVERIFY(QSslSocket::supportedCiphers().contains(socket->sessionCipher())); + socket->disconnectFromHost(); + QVERIFY(socket->waitForDisconnected()); +} + +void tst_QSslSocket::flush() +{ +} + +void tst_QSslSocket::isEncrypted() +{ +} + +void tst_QSslSocket::localCertificate() +{ + if (!QSslSocket::supportsSsl()) + return; + + // This test does not make 100% sense yet. We just set some local CA/cert/key and use it + // to authenticate ourselves against the server. The server does not actually check this + // values. This test should just run the codepath inside qsslsocket_openssl.cpp + + QSslSocketPtr socket = newSocket(); + QList localCert = QSslCertificate::fromPath(SRCDIR "certs/qt-test-server-cacert.pem"); + socket->setCaCertificates(localCert); + socket->setLocalCertificate(QLatin1String(SRCDIR "certs/fluke.cert")); + socket->setPrivateKey(QLatin1String(SRCDIR "certs/fluke.key")); + + socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443); + QVERIFY(socket->waitForEncrypted(10000)); +} + +void tst_QSslSocket::mode() +{ +} + +void tst_QSslSocket::peerCertificate() +{ +} + +void tst_QSslSocket::peerCertificateChain() +{ + if (!QSslSocket::supportsSsl()) + return; + + QSslSocketPtr socket = newSocket(); + this->socket = socket; + + QList caCertificates = QSslCertificate::fromPath(QLatin1String(SRCDIR "certs/qt-test-server-cacert.pem")); + QVERIFY(caCertificates.count() == 1); + socket->addCaCertificates(caCertificates); +#ifdef QSSLSOCKET_CERTUNTRUSTED_WORKAROUND + connect(socket, SIGNAL(sslErrors(QList)), + this, SLOT(untrustedWorkaroundSlot(QList))); +#endif + + socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443); + QCOMPARE(socket->mode(), QSslSocket::UnencryptedMode); + QVERIFY(socket->peerCertificateChain().isEmpty()); + QVERIFY2(socket->waitForEncrypted(10000), qPrintable(socket->errorString())); + + QList certChain = socket->peerCertificateChain(); + QVERIFY(certChain.count() > 0); + QCOMPARE(certChain.first(), socket->peerCertificate()); + + socket->disconnectFromHost(); + QVERIFY(socket->waitForDisconnected()); + + // connect again to a different server + socket->connectToHostEncrypted("trolltech.com", 443); + socket->ignoreSslErrors(); + QCOMPARE(socket->mode(), QSslSocket::UnencryptedMode); + QVERIFY(socket->peerCertificateChain().isEmpty()); + QVERIFY2(socket->waitForEncrypted(10000), qPrintable(socket->errorString())); + + QCOMPARE(socket->peerCertificateChain().first(), socket->peerCertificate()); + QVERIFY(socket->peerCertificateChain() != certChain); + + socket->disconnectFromHost(); + QVERIFY(socket->waitForDisconnected()); + + // now do it again back to the original server + socket->connectToHost(QtNetworkSettings::serverName(), 443); + QCOMPARE(socket->mode(), QSslSocket::UnencryptedMode); + QVERIFY(socket->peerCertificateChain().isEmpty()); + QVERIFY2(socket->waitForConnected(10000), "Network timeout"); + + socket->startClientEncryption(); + QVERIFY2(socket->waitForEncrypted(10000), qPrintable(socket->errorString())); + + QCOMPARE(socket->peerCertificateChain().first(), socket->peerCertificate()); + QVERIFY(socket->peerCertificateChain() == certChain); + + socket->disconnectFromHost(); + QVERIFY(socket->waitForDisconnected()); +} + +void tst_QSslSocket::privateKey() +{ +} + +void tst_QSslSocket::privateKeyOpaque() +{ + if (!QSslSocket::supportsSsl()) + return; + + QFile file(SRCDIR "certs/fluke.key"); + QVERIFY(file.open(QIODevice::ReadOnly)); + QSslKey key(file.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); + QVERIFY(!key.isNull()); + + EVP_PKEY *pkey = q_EVP_PKEY_new(); + q_EVP_PKEY_set1_RSA(pkey, reinterpret_cast(key.handle())); + + // This test does not make 100% sense yet. We just set some local CA/cert/key and use it + // to authenticate ourselves against the server. The server does not actually check this + // values. This test should just run the codepath inside qsslsocket_openssl.cpp + + QSslSocketPtr socket = newSocket(); + QList localCert = QSslCertificate::fromPath(SRCDIR "certs/qt-test-server-cacert.pem"); + socket->setCaCertificates(localCert); + socket->setLocalCertificate(QLatin1String(SRCDIR "certs/fluke.cert")); + socket->setPrivateKey(QSslKey(reinterpret_cast(pkey))); + + socket->setPeerVerifyMode(QSslSocket::QueryPeer); + socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443); + QVERIFY(socket->waitForEncrypted(10000)); +} + +void tst_QSslSocket::protocol() +{ + if (!QSslSocket::supportsSsl()) + return; + + QSslSocketPtr socket = newSocket(); + this->socket = socket; + QList certs = QSslCertificate::fromPath(SRCDIR "certs/qt-test-server-cacert.pem"); + + socket->setCaCertificates(certs); +#ifdef QSSLSOCKET_CERTUNTRUSTED_WORKAROUND + connect(socket, SIGNAL(sslErrors(QList)), + this, SLOT(untrustedWorkaroundSlot(QList))); +#endif + + QCOMPARE(socket->protocol(), QSsl::SecureProtocols); + { + // Fluke allows SSLv3. + socket->setProtocol(QSsl::SslV3); + QCOMPARE(socket->protocol(), QSsl::SslV3); + socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443); + QVERIFY2(socket->waitForEncrypted(), qPrintable(socket->errorString())); + QCOMPARE(socket->protocol(), QSsl::SslV3); + socket->abort(); + QCOMPARE(socket->protocol(), QSsl::SslV3); + socket->connectToHost(QtNetworkSettings::serverName(), 443); + QVERIFY2(socket->waitForConnected(), qPrintable(socket->errorString())); + socket->startClientEncryption(); + QVERIFY2(socket->waitForEncrypted(), qPrintable(socket->errorString())); + QCOMPARE(socket->protocol(), QSsl::SslV3); + socket->abort(); + } + { + // Fluke allows TLSV1. + socket->setProtocol(QSsl::TlsV1); + QCOMPARE(socket->protocol(), QSsl::TlsV1); + socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443); + QVERIFY2(socket->waitForEncrypted(), qPrintable(socket->errorString())); + QCOMPARE(socket->protocol(), QSsl::TlsV1); + socket->abort(); + QCOMPARE(socket->protocol(), QSsl::TlsV1); + socket->connectToHost(QtNetworkSettings::serverName(), 443); + QVERIFY2(socket->waitForConnected(), qPrintable(socket->errorString())); + socket->startClientEncryption(); + QVERIFY2(socket->waitForEncrypted(), qPrintable(socket->errorString())); + QCOMPARE(socket->protocol(), QSsl::TlsV1); + socket->abort(); + } + { + // Fluke allows SSLV2. + socket->setProtocol(QSsl::SslV2); + QCOMPARE(socket->protocol(), QSsl::SslV2); + socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443); + QVERIFY(socket->waitForEncrypted()); + QCOMPARE(socket->protocol(), QSsl::SslV2); + socket->abort(); + QCOMPARE(socket->protocol(), QSsl::SslV2); + socket->connectToHost(QtNetworkSettings::serverName(), 443); + QVERIFY2(socket->waitForConnected(), qPrintable(socket->errorString())); + socket->startClientEncryption(); + QVERIFY2(socket->waitForEncrypted(), qPrintable(socket->errorString())); + socket->abort(); + } + { + // Fluke allows SSLV3, so it allows AnyProtocol. + socket->setProtocol(QSsl::AnyProtocol); + QCOMPARE(socket->protocol(), QSsl::AnyProtocol); + socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443); + QVERIFY(socket->waitForEncrypted()); + QCOMPARE(socket->protocol(), QSsl::AnyProtocol); + socket->abort(); + QCOMPARE(socket->protocol(), QSsl::AnyProtocol); + socket->connectToHost(QtNetworkSettings::serverName(), 443); + QVERIFY2(socket->waitForConnected(), qPrintable(socket->errorString())); + socket->startClientEncryption(); + QVERIFY2(socket->waitForEncrypted(), qPrintable(socket->errorString())); + QCOMPARE(socket->protocol(), QSsl::AnyProtocol); + socket->abort(); + } + { + // Fluke allows SSLV3, so it allows NoSslV2 + socket->setProtocol(QSsl::TlsV1SslV3); + QCOMPARE(socket->protocol(), QSsl::TlsV1SslV3); + socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443); + QVERIFY(socket->waitForEncrypted()); + QCOMPARE(socket->protocol(), QSsl::TlsV1SslV3); + socket->abort(); + QCOMPARE(socket->protocol(), QSsl::TlsV1SslV3); + socket->connectToHost(QtNetworkSettings::serverName(), 443); + QVERIFY2(socket->waitForConnected(), qPrintable(socket->errorString())); + socket->startClientEncryption(); + QVERIFY2(socket->waitForEncrypted(), qPrintable(socket->errorString())); + QCOMPARE(socket->protocol(), QSsl::TlsV1SslV3); + socket->abort(); + } +} + +class SslServer : public QTcpServer +{ + Q_OBJECT +public: + SslServer(const QString &keyFile = SRCDIR "certs/fluke.key", const QString &certFile = SRCDIR "certs/fluke.cert") + : socket(0), + protocol(QSsl::TlsV1), + m_keyFile(keyFile), + m_certFile(certFile) { } + QSslSocket *socket; + QSsl::SslProtocol protocol; + QString m_keyFile; + QString m_certFile; + +protected: + void incomingConnection(int socketDescriptor) + { + socket = new QSslSocket(this); + socket->setProtocol(protocol); + connect(socket, SIGNAL(sslErrors(const QList &)), this, SLOT(ignoreErrorSlot())); + + QFile file(m_keyFile); + QVERIFY(file.open(QIODevice::ReadOnly)); + QSslKey key(file.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); + QVERIFY(!key.isNull()); + socket->setPrivateKey(key); + + QList localCert = QSslCertificate::fromPath(m_certFile); + QVERIFY(!localCert.isEmpty()); + QVERIFY(localCert.first().handle()); + socket->setLocalCertificate(localCert.first()); + + QVERIFY(socket->setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState)); + QVERIFY(!socket->peerAddress().isNull()); + QVERIFY(socket->peerPort() != 0); + QVERIFY(!socket->localAddress().isNull()); + QVERIFY(socket->localPort() != 0); + + socket->startServerEncryption(); + } + +protected slots: + void ignoreErrorSlot() + { + socket->ignoreSslErrors(); + } +}; + +void tst_QSslSocket::protocolServerSide_data() +{ + + QTest::addColumn("serverProtocol"); + QTest::addColumn("clientProtocol"); + QTest::addColumn("works"); + + QTest::newRow("ssl2-ssl2") << QSsl::SslV2 << QSsl::SslV2 << false; // no idea why it does not work, but we don't care about SSL 2 + QTest::newRow("ssl3-ssl3") << QSsl::SslV3 << QSsl::SslV3 << true; + QTest::newRow("tls1-tls1") << QSsl::TlsV1 << QSsl::TlsV1 << true; + QTest::newRow("tls1ssl3-tls1ssl3") << QSsl::TlsV1SslV3 << QSsl::TlsV1SslV3 << true; + QTest::newRow("any-any") << QSsl::AnyProtocol << QSsl::AnyProtocol << true; + QTest::newRow("secure-secure") << QSsl::SecureProtocols << QSsl::SecureProtocols << true; + + QTest::newRow("ssl2-ssl3") << QSsl::SslV2 << QSsl::SslV3 << false; + QTest::newRow("ssl2-tls1") << QSsl::SslV2 << QSsl::TlsV1 << false; + QTest::newRow("ssl2-tls1ssl3") << QSsl::SslV2 << QSsl::TlsV1SslV3 << false; + QTest::newRow("ssl2-secure") << QSsl::SslV2 << QSsl::SecureProtocols << false; + QTest::newRow("ssl2-any") << QSsl::SslV2 << QSsl::AnyProtocol << false; // no idea why it does not work, but we don't care about SSL 2 + + QTest::newRow("ssl3-ssl2") << QSsl::SslV3 << QSsl::SslV2 << false; + QTest::newRow("ssl3-tls1") << QSsl::SslV3 << QSsl::TlsV1 << false; + QTest::newRow("ssl3-tls1ssl3") << QSsl::SslV3 << QSsl::TlsV1SslV3 << true; + QTest::newRow("ssl3-secure") << QSsl::SslV3 << QSsl::SecureProtocols << true; + QTest::newRow("ssl3-any") << QSsl::SslV3 << QSsl::AnyProtocol << false; // we wont set a SNI header here because we connect to a + // numerical IP, so OpenSSL will send a SSL 2 handshake + + QTest::newRow("tls1-ssl2") << QSsl::TlsV1 << QSsl::SslV2 << false; + QTest::newRow("tls1-ssl3") << QSsl::TlsV1 << QSsl::SslV3 << false; + QTest::newRow("tls1-tls1ssl3") << QSsl::TlsV1 << QSsl::TlsV1SslV3 << true; + QTest::newRow("tls1-secure") << QSsl::TlsV1 << QSsl::SecureProtocols << true; + QTest::newRow("tls1-any") << QSsl::TlsV1 << QSsl::AnyProtocol << false; // we wont set a SNI header here because we connect to a + // numerical IP, so OpenSSL will send a SSL 2 handshake + + QTest::newRow("tls1ssl3-ssl2") << QSsl::TlsV1SslV3 << QSsl::SslV2 << false; + QTest::newRow("tls1ssl3-ssl3") << QSsl::TlsV1SslV3 << QSsl::SslV3 << true; + QTest::newRow("tls1ssl3-tls1") << QSsl::TlsV1SslV3 << QSsl::TlsV1 << true; + QTest::newRow("tls1ssl3-secure") << QSsl::TlsV1SslV3 << QSsl::SecureProtocols << true; + QTest::newRow("tls1ssl3-any") << QSsl::TlsV1SslV3 << QSsl::AnyProtocol << true; + + QTest::newRow("secure-ssl2") << QSsl::SecureProtocols << QSsl::SslV2 << false; + QTest::newRow("secure-ssl3") << QSsl::SecureProtocols << QSsl::SslV3 << true; + QTest::newRow("secure-tls1") << QSsl::SecureProtocols << QSsl::TlsV1 << true; + QTest::newRow("secure-tls1ssl3") << QSsl::SecureProtocols << QSsl::TlsV1SslV3 << true; + QTest::newRow("secure-any") << QSsl::SecureProtocols << QSsl::AnyProtocol << true; + + QTest::newRow("any-ssl2") << QSsl::AnyProtocol << QSsl::SslV2 << false; // no idea why it does not work, but we don't care about SSL 2 + QTest::newRow("any-ssl3") << QSsl::AnyProtocol << QSsl::SslV3 << true; + QTest::newRow("any-tls1") << QSsl::AnyProtocol << QSsl::TlsV1 << true; + QTest::newRow("any-tls1ssl3") << QSsl::AnyProtocol << QSsl::TlsV1SslV3 << true; + QTest::newRow("any-secure") << QSsl::AnyProtocol << QSsl::SecureProtocols << true; +} + +void tst_QSslSocket::protocolServerSide() +{ + if (!QSslSocket::supportsSsl()) { + qWarning("SSL not supported, skipping test"); + return; + } + + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + QFETCH(QSsl::SslProtocol, serverProtocol); + SslServer server; + server.protocol = serverProtocol; + QVERIFY(server.listen()); + + QEventLoop loop; + QTimer::singleShot(5000, &loop, SLOT(quit())); + + QSslSocketPtr client = new QSslSocket; + socket = client; + QFETCH(QSsl::SslProtocol, clientProtocol); + socket->setProtocol(clientProtocol); + // upon SSL wrong version error, error will be triggered, not sslErrors + connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), &loop, SLOT(quit())); + connect(socket, SIGNAL(sslErrors(const QList &)), this, SLOT(ignoreErrorSlot())); + connect(client, SIGNAL(encrypted()), &loop, SLOT(quit())); + + client->connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort()); + + loop.exec(); + + QFETCH(bool, works); + QAbstractSocket::SocketState expectedState = (works) ? QAbstractSocket::ConnectedState : QAbstractSocket::UnconnectedState; + QCOMPARE(client->state(), expectedState); + QCOMPARE(client->isEncrypted(), works); +} + +void tst_QSslSocket::setCaCertificates() +{ + if (!QSslSocket::supportsSsl()) + return; + + QSslSocket socket; + QCOMPARE(socket.caCertificates(), QSslSocket::defaultCaCertificates()); + socket.setCaCertificates(QSslCertificate::fromPath(SRCDIR "certs/qt-test-server-cacert.pem")); + QCOMPARE(socket.caCertificates().size(), 1); + socket.setCaCertificates(socket.defaultCaCertificates()); + QCOMPARE(socket.caCertificates(), QSslSocket::defaultCaCertificates()); +} + +void tst_QSslSocket::setLocalCertificate() +{ +} + +void tst_QSslSocket::setPrivateKey() +{ +} + +void tst_QSslSocket::setSocketDescriptor() +{ + if (!QSslSocket::supportsSsl()) + return; + + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + SslServer server; + QVERIFY(server.listen()); + + QEventLoop loop; + QTimer::singleShot(5000, &loop, SLOT(quit())); + + QSslSocketPtr client = new QSslSocket; + socket = client; + connect(socket, SIGNAL(sslErrors(const QList &)), this, SLOT(ignoreErrorSlot())); + connect(client, SIGNAL(encrypted()), &loop, SLOT(quit())); + + client->connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort()); + + loop.exec(); + + QCOMPARE(client->state(), QAbstractSocket::ConnectedState); + QVERIFY(client->isEncrypted()); + QVERIFY(!client->peerAddress().isNull()); + QVERIFY(client->peerPort() != 0); + QVERIFY(!client->localAddress().isNull()); + QVERIFY(client->localPort() != 0); +} + +void tst_QSslSocket::setSslConfiguration_data() +{ + QTest::addColumn("configuration"); + QTest::addColumn("works"); + + QTest::newRow("empty") << QSslConfiguration() << false; + QSslConfiguration conf = QSslConfiguration::defaultConfiguration(); + QTest::newRow("default") << conf << false; // does not contain test server cert + QList testServerCert = QSslCertificate::fromPath(SRCDIR "certs/qt-test-server-cacert.pem"); + conf.setCaCertificates(testServerCert); + QTest::newRow("set-root-cert") << conf << true; + conf.setProtocol(QSsl::SecureProtocols); + QTest::newRow("secure") << conf << true; +} + +void tst_QSslSocket::setSslConfiguration() +{ + if (!QSslSocket::supportsSsl()) + return; + + QSslSocketPtr socket = newSocket(); + QFETCH(QSslConfiguration, configuration); + socket->setSslConfiguration(configuration); + this->socket = socket; + socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443); + QFETCH(bool, works); + QCOMPARE(socket->waitForEncrypted(10000), works); + if (works) { + socket->disconnectFromHost(); + QVERIFY2(socket->waitForDisconnected(), qPrintable(socket->errorString())); + } +} + +void tst_QSslSocket::waitForEncrypted() +{ + if (!QSslSocket::supportsSsl()) + return; + + QSslSocketPtr socket = newSocket(); + this->socket = socket; + + connect(socket, SIGNAL(sslErrors(const QList &)), this, SLOT(ignoreErrorSlot())); + socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443); + + QVERIFY(socket->waitForEncrypted(10000)); +} + +void tst_QSslSocket::waitForEncryptedMinusOne() +{ + if (!QSslSocket::supportsSsl()) + return; + + QSslSocketPtr socket = newSocket(); + this->socket = socket; + + connect(socket, SIGNAL(sslErrors(const QList &)), this, SLOT(ignoreErrorSlot())); + socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443); + + QVERIFY(socket->waitForEncrypted(-1)); +} + +void tst_QSslSocket::waitForConnectedEncryptedReadyRead() +{ + if (!QSslSocket::supportsSsl()) + return; + + QSslSocketPtr socket = newSocket(); + this->socket = socket; + + connect(socket, SIGNAL(sslErrors(const QList &)), this, SLOT(ignoreErrorSlot())); + socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 993); + +#ifdef Q_OS_SYMBIAN + QVERIFY(socket->waitForConnected(10000)); + QVERIFY(socket->waitForEncrypted(10000)); + + // dont forget to login + QCOMPARE((int) socket->write("USER ftptest\r\n"), 14); + QCOMPARE((int) socket->write("PASS ftP2Ptf\r\n"), 14); + + QVERIFY(socket->waitForReadyRead(10000)); + QVERIFY(!socket->peerCertificate().isNull()); + QVERIFY(!socket->peerCertificateChain().isEmpty()); +#else + QVERIFY(socket->waitForConnected(10000)); + QVERIFY(socket->waitForEncrypted(10000)); + QVERIFY(socket->waitForReadyRead(10000)); + QVERIFY(!socket->peerCertificate().isNull()); + QVERIFY(!socket->peerCertificateChain().isEmpty()); +#endif +} + +void tst_QSslSocket::startClientEncryption() +{ +} + +void tst_QSslSocket::startServerEncryption() +{ +} + +void tst_QSslSocket::addDefaultCaCertificate() +{ + if (!QSslSocket::supportsSsl()) + return; + + // Reset the global CA chain + QSslSocket::setDefaultCaCertificates(QSslSocket::systemCaCertificates()); + + QList flukeCerts = QSslCertificate::fromPath(SRCDIR "certs/qt-test-server-cacert.pem"); + QCOMPARE(flukeCerts.size(), 1); + QList globalCerts = QSslSocket::defaultCaCertificates(); + QVERIFY(!globalCerts.contains(flukeCerts.first())); + QSslSocket::addDefaultCaCertificate(flukeCerts.first()); + QCOMPARE(QSslSocket::defaultCaCertificates().size(), globalCerts.size() + 1); + QVERIFY(QSslSocket::defaultCaCertificates().contains(flukeCerts.first())); + + // Restore the global CA chain + QSslSocket::setDefaultCaCertificates(QSslSocket::systemCaCertificates()); +} + +void tst_QSslSocket::addDefaultCaCertificates() +{ +} + +void tst_QSslSocket::addDefaultCaCertificates2() +{ +} + +void tst_QSslSocket::defaultCaCertificates() +{ + if (!QSslSocket::supportsSsl()) + return; + + QList certs = QSslSocket::defaultCaCertificates(); + QVERIFY(certs.size() > 1); + QCOMPARE(certs, QSslSocket::systemCaCertificates()); +} + +void tst_QSslSocket::defaultCiphers() +{ +} + +void tst_QSslSocket::resetDefaultCiphers() +{ +} + +void tst_QSslSocket::setDefaultCaCertificates() +{ +} + +void tst_QSslSocket::setDefaultCiphers() +{ +} + +void tst_QSslSocket::supportedCiphers() +{ + if (!QSslSocket::supportsSsl()) + return; + + QList ciphers = QSslSocket::supportedCiphers(); + QVERIFY(ciphers.size() > 1); + + QSslSocket socket; + QCOMPARE(socket.supportedCiphers(), ciphers); + QCOMPARE(socket.defaultCiphers(), ciphers); + QCOMPARE(socket.ciphers(), ciphers); +} + +void tst_QSslSocket::systemCaCertificates() +{ + if (!QSslSocket::supportsSsl()) + return; + + QList certs = QSslSocket::systemCaCertificates(); + QVERIFY(certs.size() > 1); + QCOMPARE(certs, QSslSocket::defaultCaCertificates()); +} + +void tst_QSslSocket::wildcardCertificateNames() +{ + // Passing CN matches + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("www.example.com"), QString("www.example.com")), true ); + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*.example.com"), QString("www.example.com")), true ); + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("xxx*.example.com"), QString("xxxwww.example.com")), true ); + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("f*.example.com"), QString("foo.example.com")), true ); + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("192.168.0.0"), QString("192.168.0.0")), true ); + + // Failing CN matches + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("xxx.example.com"), QString("www.example.com")), false ); + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*"), QString("www.example.com")), false ); + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*.*.com"), QString("www.example.com")), false ); + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*.example.com"), QString("baa.foo.example.com")), false ); + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("f*.example.com"), QString("baa.example.com")), false ); + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*.com"), QString("example.com")), false ); + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*fail.com"), QString("example.com")), false ); + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*.example."), QString("www.example.")), false ); + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*.example."), QString("www.example")), false ); + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString(""), QString("www")), false ); + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*"), QString("www")), false ); + QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*.168.0.0"), QString("192.168.0.0")), false ); +} + +void tst_QSslSocket::wildcard() +{ + QSKIP("TODO: solve wildcard problem", SkipAll); + + if (!QSslSocket::supportsSsl()) + return; + + // Fluke runs an apache server listening on port 4443, serving the + // wildcard fluke.*.troll.no. The DNS entry for + // fluke.wildcard.dev.troll.no, served by ares (root for dev.troll.no), + // returns the CNAME fluke.troll.no for this domain. The web server + // responds with the wildcard, and QSslSocket should accept that as a + // valid connection. This was broken in 4.3.0. + QSslSocketPtr socket = newSocket(); + socket->addCaCertificates(QLatin1String("certs/aspiriniks.ca.crt")); + this->socket = socket; +#ifdef QSSLSOCKET_CERTUNTRUSTED_WORKAROUND + connect(socket, SIGNAL(sslErrors(QList)), + this, SLOT(untrustedWorkaroundSlot(QList))); +#endif + socket->connectToHostEncrypted(QtNetworkSettings::wildcardServerName(), 4443); + + QVERIFY2(socket->waitForEncrypted(3000), qPrintable(socket->errorString())); + + QSslCertificate certificate = socket->peerCertificate(); + QCOMPARE(certificate.subjectInfo(QSslCertificate::CommonName), QString(QtNetworkSettings::serverLocalName() + ".*." + QtNetworkSettings::serverDomainName())); + QCOMPARE(certificate.issuerInfo(QSslCertificate::CommonName), QtNetworkSettings::serverName()); + + socket->close(); +} + +class SslServer2 : public QTcpServer +{ +protected: + void incomingConnection(int socketDescriptor) + { + QSslSocket *socket = new QSslSocket(this); + socket->ignoreSslErrors(); + + // Only set the certificate + QList localCert = QSslCertificate::fromPath(SRCDIR "certs/fluke.cert"); + QVERIFY(!localCert.isEmpty()); + QVERIFY(localCert.first().handle()); + socket->setLocalCertificate(localCert.first()); + + QVERIFY(socket->setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState)); + + socket->startServerEncryption(); + } +}; + +void tst_QSslSocket::setEmptyKey() +{ + if (!QSslSocket::supportsSsl()) + return; + + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + SslServer2 server; + server.listen(); + + QSslSocket socket; + socket.connectToHostEncrypted("127.0.0.1", server.serverPort()); + + QTestEventLoop::instance().enterLoop(2); + + QCOMPARE(socket.state(), QAbstractSocket::ConnectedState); + QCOMPARE(socket.error(), QAbstractSocket::UnknownSocketError); +} + +void tst_QSslSocket::spontaneousWrite() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + SslServer server; + QSslSocket *receiver = new QSslSocket(this); + connect(receiver, SIGNAL(readyRead()), SLOT(exitLoop())); + + // connect two sockets to each other: + QVERIFY(server.listen(QHostAddress::LocalHost)); + receiver->connectToHost("127.0.0.1", server.serverPort()); + QVERIFY(receiver->waitForConnected(5000)); + QVERIFY(server.waitForNewConnection(0)); + + QSslSocket *sender = server.socket; + QVERIFY(sender); + QVERIFY(sender->state() == QAbstractSocket::ConnectedState); + receiver->setObjectName("receiver"); + sender->setObjectName("sender"); + receiver->ignoreSslErrors(); + receiver->startClientEncryption(); + + // SSL handshake: + connect(receiver, SIGNAL(encrypted()), SLOT(exitLoop())); + enterLoop(1); + QVERIFY(!timeout()); + QVERIFY(sender->isEncrypted()); + QVERIFY(receiver->isEncrypted()); + + // make sure there's nothing to be received on the sender: + while (sender->waitForReadyRead(10) || receiver->waitForBytesWritten(10)) {} + + // spontaneously write something: + QByteArray data("Hello World"); + sender->write(data); + + // check if the other side receives it: + enterLoop(1); + QVERIFY(!timeout()); + QCOMPARE(receiver->bytesAvailable(), qint64(data.size())); + QCOMPARE(receiver->readAll(), data); +} + +void tst_QSslSocket::setReadBufferSize() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + SslServer server; + QSslSocket *receiver = new QSslSocket(this); + connect(receiver, SIGNAL(readyRead()), SLOT(exitLoop())); + + // connect two sockets to each other: + QVERIFY(server.listen(QHostAddress::LocalHost)); + receiver->connectToHost("127.0.0.1", server.serverPort()); + QVERIFY(receiver->waitForConnected(5000)); + QVERIFY(server.waitForNewConnection(0)); + + QSslSocket *sender = server.socket; + QVERIFY(sender); + QVERIFY(sender->state() == QAbstractSocket::ConnectedState); + receiver->setObjectName("receiver"); + sender->setObjectName("sender"); + receiver->ignoreSslErrors(); + receiver->startClientEncryption(); + + // SSL handshake: + connect(receiver, SIGNAL(encrypted()), SLOT(exitLoop())); + enterLoop(1); + QVERIFY(!timeout()); + QVERIFY(sender->isEncrypted()); + QVERIFY(receiver->isEncrypted()); + + QByteArray data(2048, 'b'); + receiver->setReadBufferSize(39 * 1024); // make it a non-multiple of the data.size() + + // saturate the incoming buffer + while (sender->state() == QAbstractSocket::ConnectedState && + receiver->state() == QAbstractSocket::ConnectedState && + receiver->bytesAvailable() < receiver->readBufferSize()) { + sender->write(data); + //qDebug() << receiver->bytesAvailable() << "<" << receiver->readBufferSize() << (receiver->bytesAvailable() < receiver->readBufferSize()); + + while (sender->bytesToWrite()) + QVERIFY(sender->waitForBytesWritten(10)); + + // drain it: + while (receiver->bytesAvailable() < receiver->readBufferSize() && + receiver->waitForReadyRead(10)) {} + } + + //qDebug() << sender->bytesToWrite() << "bytes to write"; + //qDebug() << receiver->bytesAvailable() << "bytes available"; + + // send a bit more + sender->write(data); + sender->write(data); + sender->write(data); + sender->write(data); + QVERIFY(sender->waitForBytesWritten(10)); + + qint64 oldBytesAvailable = receiver->bytesAvailable(); + + // now unset the read buffer limit and iterate + receiver->setReadBufferSize(0); + enterLoop(1); + QVERIFY(!timeout()); + + QVERIFY(receiver->bytesAvailable() > oldBytesAvailable); +} + +class SetReadBufferSize_task_250027_handler : public QObject { + Q_OBJECT +public slots: + void readyReadSlot() { + QTestEventLoop::instance().exitLoop(); + } + void waitSomeMore(QSslSocket *socket) { + QTime t; + t.start(); + while (!socket->encryptedBytesAvailable()) { + QCoreApplication::processEvents(QEventLoop::AllEvents | QEventLoop::WaitForMoreEvents, 250); + if (t.elapsed() > 1000 || socket->state() != QAbstractSocket::ConnectedState) + return; + } + } +}; + +void tst_QSslSocket::setReadBufferSize_task_250027() +{ + // do not execute this when a proxy is set. + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + QSslSocketPtr socket = newSocket(); + socket->setReadBufferSize(1000); // limit to 1 kb/sec + socket->ignoreSslErrors(); + socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443); + socket->ignoreSslErrors(); + QVERIFY(socket->waitForConnected(10*1000)); + QVERIFY(socket->waitForEncrypted(10*1000)); + + // exit the event loop as soon as we receive a readyRead() + SetReadBufferSize_task_250027_handler setReadBufferSize_task_250027_handler; + connect(socket, SIGNAL(readyRead()), &setReadBufferSize_task_250027_handler, SLOT(readyReadSlot())); + + // provoke a response by sending a request + socket->write("GET /qtest/fluke.gif HTTP/1.0\n"); // this file is 27 KB + socket->write("Host: "); + socket->write(QtNetworkSettings::serverName().toLocal8Bit().constData()); + socket->write("\n"); + socket->write("Connection: close\n"); + socket->write("\n"); + socket->flush(); + + QTestEventLoop::instance().enterLoop(10); + setReadBufferSize_task_250027_handler.waitSomeMore(socket); + QByteArray firstRead = socket->readAll(); + // First read should be some data, but not the whole file + QVERIFY(firstRead.size() > 0 && firstRead.size() < 20*1024); + + QTestEventLoop::instance().enterLoop(10); + setReadBufferSize_task_250027_handler.waitSomeMore(socket); + QByteArray secondRead = socket->readAll(); + // second read should be some more data + QVERIFY(secondRead.size() > 0); + + socket->close(); +} + +class SslServer3 : public QTcpServer +{ + Q_OBJECT +public: + SslServer3() : socket(0) { } + QSslSocket *socket; + +protected: + void incomingConnection(int socketDescriptor) + { + socket = new QSslSocket(this); + connect(socket, SIGNAL(sslErrors(const QList &)), this, SLOT(ignoreErrorSlot())); + + QFile file(SRCDIR "certs/fluke.key"); + QVERIFY(file.open(QIODevice::ReadOnly)); + QSslKey key(file.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); + QVERIFY(!key.isNull()); + socket->setPrivateKey(key); + + QList localCert = QSslCertificate::fromPath(SRCDIR "certs/fluke.cert"); + QVERIFY(!localCert.isEmpty()); + QVERIFY(localCert.first().handle()); + socket->setLocalCertificate(localCert.first()); + + QVERIFY(socket->setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState)); + QVERIFY(!socket->peerAddress().isNull()); + QVERIFY(socket->peerPort() != 0); + QVERIFY(!socket->localAddress().isNull()); + QVERIFY(socket->localPort() != 0); + } + +protected slots: + void ignoreErrorSlot() + { + socket->ignoreSslErrors(); + } +}; + +class ThreadedSslServer: public QThread +{ + Q_OBJECT +public: + QSemaphore dataReadSemaphore; + int serverPort; + bool ok; + + ThreadedSslServer() : serverPort(-1), ok(false) + { } + + ~ThreadedSslServer() + { + if (isRunning()) wait(2000); + QVERIFY(ok); + } + +signals: + void listening(); + +protected: + void run() + { + // if all goes well (no timeouts), this thread will sleep for a total of 500 ms + // (i.e., 5 times 100 ms, one sleep for each operation) + + SslServer3 server; + server.listen(QHostAddress::LocalHost); + serverPort = server.serverPort(); + emit listening(); + + // delayed acceptance: + QTest::qSleep(100); +#ifndef Q_OS_SYMBIAN + bool ret = server.waitForNewConnection(2000); +#else + bool ret = server.waitForNewConnection(20000); +#endif + Q_UNUSED(ret); + + // delayed start of encryption + QTest::qSleep(100); + QSslSocket *socket = server.socket; + if (!socket || !socket->isValid()) + return; // error + socket->ignoreSslErrors(); + socket->startServerEncryption(); + if (!socket->waitForEncrypted(2000)) + return; // error + + // delayed reading data + QTest::qSleep(100); + if (!socket->waitForReadyRead(2000)) + return; // error + socket->readAll(); + dataReadSemaphore.release(); + + // delayed sending data + QTest::qSleep(100); + socket->write("Hello, World"); + while (socket->bytesToWrite()) + if (!socket->waitForBytesWritten(2000)) + return; // error + + // delayed replying (reading then sending) + QTest::qSleep(100); + if (!socket->waitForReadyRead(2000)) + return; // error + socket->write("Hello, World"); + while (socket->bytesToWrite()) + if (!socket->waitForBytesWritten(2000)) + return; // error + + // delayed disconnection: + QTest::qSleep(100); + socket->disconnectFromHost(); + if (!socket->waitForDisconnected(2000)) + return; // error + + delete socket; + ok = true; + } +}; + +void tst_QSslSocket::waitForMinusOne() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + ThreadedSslServer server; + connect(&server, SIGNAL(listening()), SLOT(exitLoop())); + + // start the thread and wait for it to be ready + server.start(); + enterLoop(1); + QVERIFY(!timeout()); + + // connect to the server + QSslSocket socket; + QTest::qSleep(100); + socket.connectToHost("127.0.0.1", server.serverPort); + QVERIFY(socket.waitForConnected(-1)); + socket.ignoreSslErrors(); + socket.startClientEncryption(); + + // first verification: this waiting should take 200 ms + QVERIFY2(socket.waitForEncrypted(-1), qPrintable(socket.errorString())); + QVERIFY(socket.isEncrypted()); + QCOMPARE(socket.state(), QAbstractSocket::ConnectedState); + QCOMPARE(socket.bytesAvailable(), Q_INT64_C(0)); + + // second verification: write and make sure the other side got it (100 ms) + socket.write("How are you doing?"); + QVERIFY(socket.bytesToWrite() != 0); + QVERIFY(socket.waitForBytesWritten(-1)); + QVERIFY(server.dataReadSemaphore.tryAcquire(1, 2000)); + + // third verification: it should wait for 100 ms: + QVERIFY(socket.waitForReadyRead(-1)); + QVERIFY(socket.isEncrypted()); + QCOMPARE(socket.state(), QAbstractSocket::ConnectedState); + QVERIFY(socket.bytesAvailable() != 0); + + // fourth verification: deadlock prevention: + // we write and then wait for reading; the other side needs to receive before + // replying (100 ms delay) + socket.write("I'm doing just fine!"); + QVERIFY(socket.bytesToWrite() != 0); + QVERIFY(socket.waitForReadyRead(-1)); + + // fifth verification: it should wait for 200 ms more + QVERIFY(socket.waitForDisconnected(-1)); +} + +class VerifyServer : public QTcpServer +{ + Q_OBJECT +public: + VerifyServer() : socket(0) { } + QSslSocket *socket; + +protected: + void incomingConnection(int socketDescriptor) + { + socket = new QSslSocket(this); + + socket->setPrivateKey(SRCDIR "certs/fluke.key"); + socket->setLocalCertificate(SRCDIR "certs/fluke.cert"); + socket->setSocketDescriptor(socketDescriptor); + socket->startServerEncryption(); + } +}; + +void tst_QSslSocket::verifyMode() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + QSslSocket socket; + QCOMPARE(socket.peerVerifyMode(), QSslSocket::AutoVerifyPeer); + socket.setPeerVerifyMode(QSslSocket::VerifyNone); + QCOMPARE(socket.peerVerifyMode(), QSslSocket::VerifyNone); + socket.setPeerVerifyMode(QSslSocket::VerifyNone); + socket.setPeerVerifyMode(QSslSocket::VerifyPeer); + QCOMPARE(socket.peerVerifyMode(), QSslSocket::VerifyPeer); + + socket.connectToHostEncrypted(QtNetworkSettings::serverName(), 443); + QVERIFY(!socket.waitForEncrypted()); + + QList expectedErrors = QList() + << QSslError(QSslError::SelfSignedCertificate, socket.peerCertificate()); + QCOMPARE(socket.sslErrors(), expectedErrors); + socket.abort(); + + VerifyServer server; + server.listen(); + + QSslSocket clientSocket; + clientSocket.connectToHostEncrypted("127.0.0.1", server.serverPort()); + clientSocket.ignoreSslErrors(); + + QEventLoop loop; + QTimer::singleShot(5000, &loop, SLOT(quit())); + connect(&clientSocket, SIGNAL(encrypted()), &loop, SLOT(quit())); + loop.exec(); + + QVERIFY(clientSocket.isEncrypted()); + QVERIFY(server.socket->sslErrors().isEmpty()); +} + +void tst_QSslSocket::verifyDepth() +{ + QSslSocket socket; + QCOMPARE(socket.peerVerifyDepth(), 0); + socket.setPeerVerifyDepth(1); + QCOMPARE(socket.peerVerifyDepth(), 1); + QTest::ignoreMessage(QtWarningMsg, "QSslSocket::setPeerVerifyDepth: cannot set negative depth of -1"); + socket.setPeerVerifyDepth(-1); + QCOMPARE(socket.peerVerifyDepth(), 1); +} + +void tst_QSslSocket::peerVerifyError() +{ + QSslSocketPtr socket = newSocket(); + QSignalSpy sslErrorsSpy(socket, SIGNAL(sslErrors(QList))); + QSignalSpy peerVerifyErrorSpy(socket, SIGNAL(peerVerifyError(QSslError))); + + socket->connectToHostEncrypted(QHostInfo::fromName(QtNetworkSettings::serverName()).addresses().first().toString(), 443); + QVERIFY(!socket->waitForEncrypted(10000)); + QVERIFY(!peerVerifyErrorSpy.isEmpty()); + QVERIFY(!sslErrorsSpy.isEmpty()); + QCOMPARE(qVariantValue(peerVerifyErrorSpy.last().at(0)).error(), QSslError::HostNameMismatch); + QCOMPARE(qVariantValue >(sslErrorsSpy.at(0).at(0)).size(), peerVerifyErrorSpy.size()); +} + +void tst_QSslSocket::disconnectFromHostWhenConnecting() +{ + QSslSocketPtr socket = newSocket(); + socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 993); + socket->ignoreSslErrors(); + socket->write("XXXX LOGOUT\r\n"); + QAbstractSocket::SocketState state = socket->state(); + // without proxy, the state will be HostLookupState; + // with proxy, the state will be ConnectingState. + QVERIFY(socket->state() == QAbstractSocket::HostLookupState || + socket->state() == QAbstractSocket::ConnectingState); + socket->disconnectFromHost(); + // the state of the socket must be the same before and after calling + // disconnectFromHost() + QCOMPARE(state, socket->state()); + QVERIFY(socket->state() == QAbstractSocket::HostLookupState || + socket->state() == QAbstractSocket::ConnectingState); + QVERIFY(socket->waitForDisconnected(10000)); + QCOMPARE(socket->state(), QAbstractSocket::UnconnectedState); + // we did not call close, so the socket must be still open + QVERIFY(socket->isOpen()); + QCOMPARE(socket->bytesToWrite(), qint64(0)); + + // dont forget to login + QCOMPARE((int) socket->write("USER ftptest\r\n"), 14); + +} + +void tst_QSslSocket::disconnectFromHostWhenConnected() +{ + QSslSocketPtr socket = newSocket(); + socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 993); + socket->ignoreSslErrors(); +#ifndef Q_OS_SYMBIAN + QVERIFY(socket->waitForEncrypted(5000)); +#else + QVERIFY(socket->waitForEncrypted(10000)); +#endif + socket->write("XXXX LOGOUT\r\n"); + QCOMPARE(socket->state(), QAbstractSocket::ConnectedState); + socket->disconnectFromHost(); + QCOMPARE(socket->state(), QAbstractSocket::ClosingState); +#ifdef Q_OS_SYMBIAN + // I don't understand how socket->waitForDisconnected can work on other platforms + // since socket->write will end to: + // QMetaObject::invokeMethod(this, "_q_flushWriteBuffer", Qt::QueuedConnection); + // In order that _q_flushWriteBuffer will be called the eventloop need to run + // If we just call waitForDisconnected, which blocks the whole thread how that can happen? + connect(socket, SIGNAL(disconnected()), this, SLOT(exitLoop())); + enterLoop(5); + QVERIFY(!timeout()); +#else + QVERIFY(socket->waitForDisconnected(5000)); +#endif + QCOMPARE(socket->bytesToWrite(), qint64(0)); +} + +void tst_QSslSocket::resetProxy() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + // check fix for bug 199941 + + QNetworkProxy goodProxy(QNetworkProxy::NoProxy); + QNetworkProxy badProxy(QNetworkProxy::HttpProxy, "thisCannotWorkAbsolutelyNotForSure", 333); + + // make sure the connection works, and then set a nonsense proxy, and then + // make sure it does not work anymore + QSslSocket socket; + socket.addCaCertificates(QLatin1String(SRCDIR "certs/qt-test-server-cacert.pem")); + socket.setProxy(goodProxy); + socket.connectToHostEncrypted(QtNetworkSettings::serverName(), 443); + QVERIFY2(socket.waitForConnected(10000), qPrintable(socket.errorString())); + socket.abort(); + socket.setProxy(badProxy); + socket.connectToHostEncrypted(QtNetworkSettings::serverName(), 443); + QVERIFY(! socket.waitForConnected(10000)); + + // dont forget to login + QCOMPARE((int) socket.write("USER ftptest\r\n"), 14); + QCOMPARE((int) socket.write("PASS password\r\n"), 15); + + enterLoop(10); + + // now the other way round: + // set the nonsense proxy and make sure the connection does not work, + // and then set the right proxy and make sure it works + QSslSocket socket2; + socket2.addCaCertificates(QLatin1String(SRCDIR "certs/qt-test-server-cacert.pem")); + socket2.setProxy(badProxy); + socket2.connectToHostEncrypted(QtNetworkSettings::serverName(), 443); + QVERIFY(! socket2.waitForConnected(10000)); + socket2.abort(); + socket2.setProxy(goodProxy); + socket2.connectToHostEncrypted(QtNetworkSettings::serverName(), 443); + QVERIFY2(socket2.waitForConnected(10000), qPrintable(socket.errorString())); +} + +void tst_QSslSocket::ignoreSslErrorsList_data() +{ + QTest::addColumn >("expectedSslErrors"); + QTest::addColumn("expectedSslErrorSignalCount"); + + // construct the list of errors that we will get with the SSL handshake and that we will ignore + QList expectedSslErrors; + // fromPath gives us a list of certs, but it actually only contains one + QList certs = QSslCertificate::fromPath(QLatin1String(SRCDIR "certs/qt-test-server-cacert.pem")); + QSslError rightError(QSslError::SelfSignedCertificate, certs.at(0)); + QSslError wrongError(QSslError::SelfSignedCertificate); + + + QTest::newRow("SSL-failure-empty-list") << expectedSslErrors << 1; + expectedSslErrors.append(wrongError); + QTest::newRow("SSL-failure-wrong-error") << expectedSslErrors << 1; + expectedSslErrors.append(rightError); + QTest::newRow("allErrorsInExpectedList1") << expectedSslErrors << 0; + expectedSslErrors.removeAll(wrongError); + QTest::newRow("allErrorsInExpectedList2") << expectedSslErrors << 0; + expectedSslErrors.removeAll(rightError); + QTest::newRow("SSL-failure-empty-list-again") << expectedSslErrors << 1; +} + +void tst_QSslSocket::ignoreSslErrorsList() +{ + QSslSocket socket; + connect(&socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + +// this->socket = &socket; + QSslCertificate cert; + + QFETCH(QList, expectedSslErrors); + socket.ignoreSslErrors(expectedSslErrors); + + QFETCH(int, expectedSslErrorSignalCount); + QSignalSpy sslErrorsSpy(&socket, SIGNAL(error(QAbstractSocket::SocketError))); + + socket.connectToHostEncrypted(QtNetworkSettings::serverName(), 443); + + bool expectEncryptionSuccess = (expectedSslErrorSignalCount == 0); + QCOMPARE(socket.waitForEncrypted(10000), expectEncryptionSuccess); + QCOMPARE(sslErrorsSpy.count(), expectedSslErrorSignalCount); +} + +void tst_QSslSocket::ignoreSslErrorsListWithSlot_data() +{ + ignoreSslErrorsList_data(); +} + +// this is not a test, just a slot called in the test below +void tst_QSslSocket::ignoreErrorListSlot(const QList &) +{ + socket->ignoreSslErrors(storedExpectedSslErrors); +} + +void tst_QSslSocket::ignoreSslErrorsListWithSlot() +{ + QSslSocket socket; + this->socket = &socket; + + QFETCH(QList, expectedSslErrors); + // store the errors to ignore them later in the slot connected below + storedExpectedSslErrors = expectedSslErrors; + connect(&socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + connect(&socket, SIGNAL(sslErrors(const QList &)), + this, SLOT(ignoreErrorListSlot(const QList &))); + socket.connectToHostEncrypted(QtNetworkSettings::serverName(), 443); + + QFETCH(int, expectedSslErrorSignalCount); + bool expectEncryptionSuccess = (expectedSslErrorSignalCount == 0); + QCOMPARE(socket.waitForEncrypted(10000), expectEncryptionSuccess); +} + +// make sure a closed socket has no bytesAvailable() +// related to https://bugs.webkit.org/show_bug.cgi?id=28016 +void tst_QSslSocket::readFromClosedSocket() +{ + QSslSocketPtr socket = newSocket(); + socket->ignoreSslErrors(); + socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443); + socket->ignoreSslErrors(); + socket->waitForConnected(); + socket->waitForEncrypted(); + // provoke a response by sending a request + socket->write("GET /qtest/fluke.gif HTTP/1.1\n"); + socket->write("Host: "); + socket->write(QtNetworkSettings::serverName().toLocal8Bit().constData()); + socket->write("\n"); + socket->write("\n"); + socket->waitForBytesWritten(); + socket->waitForReadyRead(); + QVERIFY(socket->state() == QAbstractSocket::ConnectedState); + QVERIFY(socket->bytesAvailable()); + socket->close(); + QVERIFY(!socket->bytesAvailable()); + QVERIFY(!socket->bytesToWrite()); + QVERIFY(socket->state() == QAbstractSocket::UnconnectedState); +} + +void tst_QSslSocket::writeBigChunk() +{ + if (!QSslSocket::supportsSsl()) + return; + + QSslSocketPtr socket = newSocket(); + this->socket = socket; + + connect(socket, SIGNAL(sslErrors(const QList &)), this, SLOT(ignoreErrorSlot())); + socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443); + + QByteArray data; + data.resize(1024*1024*10); // 10 MB + // init with garbage. needed so ssl cannot compress it in an efficient way. + for (size_t i = 0; i < data.size() / sizeof(int); i++) { + int r = qrand(); + data.data()[i*sizeof(int)] = r; + } + + QVERIFY(socket->waitForEncrypted(10000)); + QString errorBefore = socket->errorString(); + + int ret = socket->write(data.constData(), data.size()); + QVERIFY(data.size() == ret); + + // spin the event loop once so QSslSocket::transmit() gets called + QCoreApplication::processEvents(); + QString errorAfter = socket->errorString(); + + // no better way to do this right now since the error is the same as the default error. + if (socket->errorString().startsWith(QLatin1String("Unable to write data"))) + { + qWarning() << socket->error() << socket->errorString(); + QFAIL("Error while writing! Check if the OpenSSL BIO size is limited?!"); + } + // also check the error string. If another error (than UnknownError) occurred, it should be different than before + QVERIFY(errorBefore == errorAfter); + + // check that everything has been written to OpenSSL + QVERIFY(socket->bytesToWrite() == 0); + + socket->close(); +} + +void tst_QSslSocket::blacklistedCertificates() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + SslServer server(SRCDIR "certs/fake-login.live.com.key", SRCDIR "certs/fake-login.live.com.pem"); + QSslSocket *receiver = new QSslSocket(this); + connect(receiver, SIGNAL(readyRead()), SLOT(exitLoop())); + + // connect two sockets to each other: + QVERIFY(server.listen(QHostAddress::LocalHost)); + receiver->connectToHost("127.0.0.1", server.serverPort()); + QVERIFY(receiver->waitForConnected(5000)); + QVERIFY(server.waitForNewConnection(0)); + + QSslSocket *sender = server.socket; + QVERIFY(sender); + QVERIFY(sender->state() == QAbstractSocket::ConnectedState); + receiver->setObjectName("receiver"); + sender->setObjectName("sender"); + receiver->startClientEncryption(); + + connect(receiver, SIGNAL(sslErrors(QList)), SLOT(exitLoop())); + connect(receiver, SIGNAL(encrypted()), SLOT(exitLoop())); + enterLoop(1); + QList sslErrors = receiver->sslErrors(); + QVERIFY(sslErrors.count() > 0); + // there are more errors (self signed cert and hostname mismatch), but we only care about the blacklist error + QCOMPARE(sslErrors.at(0).error(), QSslError::CertificateBlacklisted); +} + +void tst_QSslSocket::setEmptyDefaultConfiguration() +{ + // used to produce a crash in QSslConfigurationPrivate::deepCopyDefaultConfiguration, QTBUG-13265 + + if (!QSslSocket::supportsSsl()) + return; + + QSslConfiguration emptyConf; + QSslConfiguration::setDefaultConfiguration(emptyConf); + + QSslSocketPtr socket = newSocket(); + connect(socket, SIGNAL(sslErrors(const QList &)), this, SLOT(ignoreErrorSlot())); + socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443); + QVERIFY2(!socket->waitForEncrypted(4000), qPrintable(socket->errorString())); +} + +void tst_QSslSocket::versionAccessors() +{ + if (!QSslSocket::supportsSsl()) + return; + + qDebug() << QSslSocket::sslLibraryVersionString(); + qDebug() << QString::number(QSslSocket::sslLibraryVersionNumber(), 16); +} + +#endif // QT_NO_OPENSSL + +QTEST_MAIN(tst_QSslSocket) +#include "tst_qsslsocket.moc" diff --git a/tests/auto/network/ssl/ssl.pro b/tests/auto/network/ssl/ssl.pro new file mode 100644 index 0000000000..fd57a1729b --- /dev/null +++ b/tests/auto/network/ssl/ssl.pro @@ -0,0 +1,8 @@ +TEMPLATE=subdirs +SUBDIRS=\ + qsslcertificate \ + qsslcipher \ + qsslerror \ + qsslsocket \ + qsslkey \ + -- cgit v1.2.3