summaryrefslogtreecommitdiffstats
path: root/tests/auto/network
diff options
context:
space:
mode:
authorJo Asplin <jo.asplin@nokia.com>2011-09-06 13:46:40 +0200
committerJo Asplin <jo.asplin@nokia.com>2011-09-09 09:32:17 +0200
commitc59f9ad7768a007ca7a49ea11b16617529e86d52 (patch)
treed481be2a727f8461a76c2b729fc7e93784a3cff1 /tests/auto/network
parent2d41aff1e8557a43268bce631df834bfa79593cf (diff)
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 <qt_sanity_bot@ovi.com> Reviewed-by: Sergio Ahumada <sergio.ahumada@nokia.com> Reviewed-by: Rohan McGovern <rohan.mcgovern@nokia.com>
Diffstat (limited to 'tests/auto/network')
-rw-r--r--tests/auto/network/access/access.pro20
-rw-r--r--tests/auto/network/access/qabstractnetworkcache/.gitignore1
-rw-r--r--tests/auto/network/access/qabstractnetworkcache/qabstractnetworkcache.pro14
-rwxr-xr-xtests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_cachecontrol-expire.cgi7
-rwxr-xr-xtests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_cachecontrol.cgi13
-rwxr-xr-xtests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_cachecontrol200.cgi9
-rwxr-xr-xtests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_etag200.cgi5
-rwxr-xr-xtests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_etag304.cgi11
-rwxr-xr-xtests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_expires200.cgi5
-rwxr-xr-xtests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_expires304.cgi11
-rwxr-xr-xtests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_expires500.cgi11
-rwxr-xr-xtests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_lastModified200.cgi5
-rwxr-xr-xtests/auto/network/access/qabstractnetworkcache/tests/httpcachetest_lastModified304.cgi11
-rw-r--r--tests/auto/network/access/qabstractnetworkcache/tst_qabstractnetworkcache.cpp416
-rw-r--r--tests/auto/network/access/qftp/.gitattributes1
-rw-r--r--tests/auto/network/access/qftp/.gitignore2
-rw-r--r--tests/auto/network/access/qftp/qftp.pro22
-rw-r--r--tests/auto/network/access/qftp/rfc3252.txt899
-rw-r--r--tests/auto/network/access/qftp/tst_qftp.cpp2170
-rw-r--r--tests/auto/network/access/qhttp/.gitattributes1
-rw-r--r--tests/auto/network/access/qhttp/.gitignore1
-rw-r--r--tests/auto/network/access/qhttp/dummyserver.h114
-rw-r--r--tests/auto/network/access/qhttp/qhttp.pro31
-rw-r--r--tests/auto/network/access/qhttp/rfc3252.txt899
-rw-r--r--tests/auto/network/access/qhttp/trolltech8
-rw-r--r--tests/auto/network/access/qhttp/tst_qhttp.cpp1576
-rwxr-xr-xtests/auto/network/access/qhttp/webserver/cgi-bin/retrieve_testfile.cgi6
-rwxr-xr-xtests/auto/network/access/qhttp/webserver/cgi-bin/rfc.cgi5
-rwxr-xr-xtests/auto/network/access/qhttp/webserver/cgi-bin/store_testfile.cgi6
-rw-r--r--tests/auto/network/access/qhttp/webserver/index.html899
-rw-r--r--tests/auto/network/access/qhttp/webserver/rfc3252899
-rw-r--r--tests/auto/network/access/qhttp/webserver/rfc3252.txt899
-rw-r--r--tests/auto/network/access/qhttpnetworkconnection/.gitignore1
-rw-r--r--tests/auto/network/access/qhttpnetworkconnection/qhttpnetworkconnection.pro13
-rw-r--r--tests/auto/network/access/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp1131
-rw-r--r--tests/auto/network/access/qhttpnetworkreply/.gitignore1
-rw-r--r--tests/auto/network/access/qhttpnetworkreply/qhttpnetworkreply.pro7
-rw-r--r--tests/auto/network/access/qhttpnetworkreply/tst_qhttpnetworkreply.cpp134
-rw-r--r--tests/auto/network/access/qnetworkaccessmanager/qnetworkaccessmanager.pro6
-rw-r--r--tests/auto/network/access/qnetworkaccessmanager/tst_qnetworkaccessmanager.cpp111
-rw-r--r--tests/auto/network/access/qnetworkcachemetadata/.gitignore1
-rw-r--r--tests/auto/network/access/qnetworkcachemetadata/qnetworkcachemetadata.pro7
-rw-r--r--tests/auto/network/access/qnetworkcachemetadata/tst_qnetworkcachemetadata.cpp373
-rw-r--r--tests/auto/network/access/qnetworkcookie/.gitignore1
-rw-r--r--tests/auto/network/access/qnetworkcookie/qnetworkcookie.pro5
-rw-r--r--tests/auto/network/access/qnetworkcookie/tst_qnetworkcookie.cpp733
-rw-r--r--tests/auto/network/access/qnetworkcookiejar/.gitignore1
-rw-r--r--tests/auto/network/access/qnetworkcookiejar/qnetworkcookiejar.pro5
-rw-r--r--tests/auto/network/access/qnetworkcookiejar/tst_qnetworkcookiejar.cpp446
-rw-r--r--tests/auto/network/access/qnetworkdiskcache/.gitignore1
-rw-r--r--tests/auto/network/access/qnetworkdiskcache/qnetworkdiskcache.pro7
-rw-r--r--tests/auto/network/access/qnetworkdiskcache/tst_qnetworkdiskcache.cpp677
-rw-r--r--tests/auto/network/access/qnetworkreply/.gitattributes3
-rw-r--r--tests/auto/network/access/qnetworkreply/.gitignore3
-rw-r--r--tests/auto/network/access/qnetworkreply/bigfile17980
-rw-r--r--tests/auto/network/access/qnetworkreply/certs/aspiriniks.ca.crt22
-rw-r--r--tests/auto/network/access/qnetworkreply/certs/fluke.cert75
-rw-r--r--tests/auto/network/access/qnetworkreply/certs/fluke.key15
-rw-r--r--tests/auto/network/access/qnetworkreply/certs/qt-test-server-cacert.pem17
-rw-r--r--tests/auto/network/access/qnetworkreply/certs/server.key15
-rw-r--r--tests/auto/network/access/qnetworkreply/certs/server.pem24
-rw-r--r--tests/auto/network/access/qnetworkreply/echo/echo.pro6
-rw-r--r--tests/auto/network/access/qnetworkreply/echo/main.cpp62
-rw-r--r--tests/auto/network/access/qnetworkreply/empty0
-rw-r--r--tests/auto/network/access/qnetworkreply/image1.jpgbin0 -> 1045459 bytes
-rw-r--r--tests/auto/network/access/qnetworkreply/image2.jpgbin0 -> 879218 bytes
-rw-r--r--tests/auto/network/access/qnetworkreply/image3.jpgbin0 -> 887729 bytes
-rw-r--r--tests/auto/network/access/qnetworkreply/qnetworkreply.pro7
-rw-r--r--tests/auto/network/access/qnetworkreply/qnetworkreply.qrc5
-rw-r--r--tests/auto/network/access/qnetworkreply/resource283
-rw-r--r--tests/auto/network/access/qnetworkreply/rfc3252.txt899
-rw-r--r--tests/auto/network/access/qnetworkreply/smb-file.txt1
-rw-r--r--tests/auto/network/access/qnetworkreply/test/test.pro38
-rw-r--r--tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp6342
-rw-r--r--tests/auto/network/access/qnetworkrequest/.gitignore1
-rw-r--r--tests/auto/network/access/qnetworkrequest/qnetworkrequest.pro5
-rw-r--r--tests/auto/network/access/qnetworkrequest/tst_qnetworkrequest.cpp496
-rw-r--r--tests/auto/network/bearer/bearer.pro6
-rw-r--r--tests/auto/network/bearer/qbearertestcommon.h87
-rw-r--r--tests/auto/network/bearer/qnetworkconfiguration/qnetworkconfiguration.pro15
-rw-r--r--tests/auto/network/bearer/qnetworkconfiguration/tst_qnetworkconfiguration.cpp311
-rw-r--r--tests/auto/network/bearer/qnetworkconfigurationmanager/qnetworkconfigurationmanager.pro15
-rw-r--r--tests/auto/network/bearer/qnetworkconfigurationmanager/tst_qnetworkconfigurationmanager.cpp378
-rw-r--r--tests/auto/network/bearer/qnetworksession/lackey/lackey.pro15
-rw-r--r--tests/auto/network/bearer/qnetworksession/lackey/main.cpp155
-rw-r--r--tests/auto/network/bearer/qnetworksession/qnetworksession.pro3
-rw-r--r--tests/auto/network/bearer/qnetworksession/test/test.pro26
-rw-r--r--tests/auto/network/bearer/qnetworksession/test/tst_qnetworksession.cpp1683
-rw-r--r--tests/auto/network/kernel/kernel.pro14
-rw-r--r--tests/auto/network/kernel/qauthenticator/qauthenticator.pro5
-rw-r--r--tests/auto/network/kernel/qauthenticator/tst_qauthenticator.cpp155
-rw-r--r--tests/auto/network/kernel/qhostaddress/.gitignore1
-rw-r--r--tests/auto/network/kernel/qhostaddress/qhostaddress.pro15
-rw-r--r--tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp611
-rw-r--r--tests/auto/network/kernel/qhostinfo/.gitignore1
-rw-r--r--tests/auto/network/kernel/qhostinfo/qhostinfo.pro16
-rw-r--r--tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp665
-rw-r--r--tests/auto/network/kernel/qnetworkaddressentry/.gitignore1
-rw-r--r--tests/auto/network/kernel/qnetworkaddressentry/qnetworkaddressentry.pro6
-rw-r--r--tests/auto/network/kernel/qnetworkaddressentry/tst_qnetworkaddressentry.cpp185
-rw-r--r--tests/auto/network/kernel/qnetworkinterface/.gitignore1
-rw-r--r--tests/auto/network/kernel/qnetworkinterface/qnetworkinterface.pro7
-rw-r--r--tests/auto/network/kernel/qnetworkinterface/tst_qnetworkinterface.cpp241
-rw-r--r--tests/auto/network/kernel/qnetworkproxy/.gitignore1
-rw-r--r--tests/auto/network/kernel/qnetworkproxy/qnetworkproxy.pro11
-rw-r--r--tests/auto/network/kernel/qnetworkproxy/tst_qnetworkproxy.cpp118
-rw-r--r--tests/auto/network/kernel/qnetworkproxyfactory/.gitignore1
-rw-r--r--tests/auto/network/kernel/qnetworkproxyfactory/qnetworkproxyfactory.pro11
-rw-r--r--tests/auto/network/kernel/qnetworkproxyfactory/tst_qnetworkproxyfactory.cpp275
-rw-r--r--tests/auto/network/network.pro8
-rw-r--r--tests/auto/network/socket/platformsocketengine/.gitignore1
-rw-r--r--tests/auto/network/socket/platformsocketengine/platformsocketengine.pri19
-rw-r--r--tests/auto/network/socket/platformsocketengine/platformsocketengine.pro16
-rw-r--r--tests/auto/network/socket/platformsocketengine/tst_platformsocketengine.cpp763
-rw-r--r--tests/auto/network/socket/qabstractsocket/.gitignore1
-rw-r--r--tests/auto/network/socket/qabstractsocket/qabstractsocket.pro11
-rw-r--r--tests/auto/network/socket/qabstractsocket/tst_qabstractsocket.cpp109
-rw-r--r--tests/auto/network/socket/qhttpsocketengine/.gitignore1
-rw-r--r--tests/auto/network/socket/qhttpsocketengine/qhttpsocketengine.pro13
-rw-r--r--tests/auto/network/socket/qhttpsocketengine/tst_qhttpsocketengine.cpp748
-rw-r--r--tests/auto/network/socket/qlocalsocket/.gitignore2
-rw-r--r--tests/auto/network/socket/qlocalsocket/example/client/client.pro10
-rw-r--r--tests/auto/network/socket/qlocalsocket/example/client/main.cpp84
-rw-r--r--tests/auto/network/socket/qlocalsocket/example/example.pro3
-rw-r--r--tests/auto/network/socket/qlocalsocket/example/server/main.cpp97
-rw-r--r--tests/auto/network/socket/qlocalsocket/example/server/server.pro13
-rw-r--r--tests/auto/network/socket/qlocalsocket/lackey/lackey.pro16
-rw-r--r--tests/auto/network/socket/qlocalsocket/lackey/main.cpp296
-rwxr-xr-xtests/auto/network/socket/qlocalsocket/lackey/scripts/client.js35
-rw-r--r--tests/auto/network/socket/qlocalsocket/lackey/scripts/server.js19
-rw-r--r--tests/auto/network/socket/qlocalsocket/qlocalsocket.pro4
-rw-r--r--tests/auto/network/socket/qlocalsocket/test/test.pro50
-rw-r--r--tests/auto/network/socket/qlocalsocket/tst_qlocalsocket.cpp1120
-rw-r--r--tests/auto/network/socket/qsocks5socketengine/.gitignore1
-rw-r--r--tests/auto/network/socket/qsocks5socketengine/qsocks5socketengine.pro17
-rw-r--r--tests/auto/network/socket/qsocks5socketengine/tst_qsocks5socketengine.cpp963
-rw-r--r--tests/auto/network/socket/qtcpserver/.gitignore3
-rw-r--r--tests/auto/network/socket/qtcpserver/crashingServer/crashingServer.pro9
-rw-r--r--tests/auto/network/socket/qtcpserver/crashingServer/main.cpp70
-rw-r--r--tests/auto/network/socket/qtcpserver/qtcpserver.pro4
-rw-r--r--tests/auto/network/socket/qtcpserver/test/test.pro37
-rw-r--r--tests/auto/network/socket/qtcpserver/tst_qtcpserver.cpp817
-rw-r--r--tests/auto/network/socket/qtcpsocket/.gitignore3
-rw-r--r--tests/auto/network/socket/qtcpsocket/qtcpsocket.pro8
-rw-r--r--tests/auto/network/socket/qtcpsocket/stressTest/Test.cpp146
-rw-r--r--tests/auto/network/socket/qtcpsocket/stressTest/Test.h95
-rw-r--r--tests/auto/network/socket/qtcpsocket/stressTest/main.cpp67
-rw-r--r--tests/auto/network/socket/qtcpsocket/stressTest/stressTest.pro12
-rw-r--r--tests/auto/network/socket/qtcpsocket/test/test.pro33
-rw-r--r--tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp2684
-rw-r--r--tests/auto/network/socket/qudpsocket/.gitignore2
-rw-r--r--tests/auto/network/socket/qudpsocket/clientserver/clientserver.pro8
-rw-r--r--tests/auto/network/socket/qudpsocket/clientserver/main.cpp170
-rw-r--r--tests/auto/network/socket/qudpsocket/qudpsocket.pro4
-rw-r--r--tests/auto/network/socket/qudpsocket/test/test.pro28
-rw-r--r--tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp1356
-rw-r--r--tests/auto/network/socket/qudpsocket/udpServer/main.cpp90
-rw-r--r--tests/auto/network/socket/qudpsocket/udpServer/udpServer.pro7
-rw-r--r--tests/auto/network/socket/socket.pro17
-rw-r--r--tests/auto/network/ssl/qsslcertificate/.gitignore1
-rw-r--r--tests/auto/network/ssl/qsslcertificate/certificates/ca-cert.pem33
-rw-r--r--tests/auto/network/ssl/qsslcertificate/certificates/ca-cert.pem.digest-md51
-rw-r--r--tests/auto/network/ssl/qsslcertificate/certificates/ca-cert.pem.digest-sha11
-rw-r--r--tests/auto/network/ssl/qsslcertificate/certificates/cert-ss-san-utf8.pem16
-rw-r--r--tests/auto/network/ssl/qsslcertificate/certificates/cert-ss-san-utf8.pem.san5
-rw-r--r--tests/auto/network/ssl/qsslcertificate/certificates/cert-ss-san.pem13
-rw-r--r--tests/auto/network/ssl/qsslcertificate/certificates/cert-ss-san.pem.san5
-rw-r--r--tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.derbin0 -> 461 bytes
-rw-r--r--tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.der.pubkeybin0 -> 162 bytes
-rw-r--r--tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.pem12
-rw-r--r--tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.pem.digest-md51
-rw-r--r--tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.pem.digest-sha11
-rw-r--r--tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.pem.pubkey6
-rw-r--r--tests/auto/network/ssl/qsslcertificate/certificates/cert.derbin0 -> 503 bytes
-rw-r--r--tests/auto/network/ssl/qsslcertificate/certificates/cert.der.pubkeybin0 -> 162 bytes
-rw-r--r--tests/auto/network/ssl/qsslcertificate/certificates/cert.pem13
-rw-r--r--tests/auto/network/ssl/qsslcertificate/certificates/cert.pem.digest-md51
-rw-r--r--tests/auto/network/ssl/qsslcertificate/certificates/cert.pem.digest-sha11
-rw-r--r--tests/auto/network/ssl/qsslcertificate/certificates/cert.pem.pubkey6
-rwxr-xr-xtests/auto/network/ssl/qsslcertificate/certificates/gencertificates.sh104
-rw-r--r--tests/auto/network/ssl/qsslcertificate/certificates/san.cnf5
-rw-r--r--tests/auto/network/ssl/qsslcertificate/more-certificates/badguy-nul-cn.crt81
-rw-r--r--tests/auto/network/ssl/qsslcertificate/more-certificates/badguy-nul-san.crt83
-rw-r--r--tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted-google.com-diginotar.pem30
-rw-r--r--tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted1.pem19
-rw-r--r--tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted2.pem19
-rw-r--r--tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted3.pem19
-rw-r--r--tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted4.pem19
-rw-r--r--tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted5.pem19
-rw-r--r--tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted6.pem19
-rw-r--r--tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted7.pem19
-rw-r--r--tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted8.pem19
-rw-r--r--tests/auto/network/ssl/qsslcertificate/more-certificates/blacklisted9.pem19
-rw-r--r--tests/auto/network/ssl/qsslcertificate/more-certificates/cert-large-expiration-date.pem15
-rw-r--r--tests/auto/network/ssl/qsslcertificate/more-certificates/cert-large-expiration-date.txt.0.9.842
-rw-r--r--tests/auto/network/ssl/qsslcertificate/more-certificates/cert-large-expiration-date.txt.1.0.042
-rw-r--r--tests/auto/network/ssl/qsslcertificate/more-certificates/cert-large-serial-number.pem14
-rw-r--r--tests/auto/network/ssl/qsslcertificate/more-certificates/malformed-just-begin-no-newline.pem1
-rw-r--r--tests/auto/network/ssl/qsslcertificate/more-certificates/malformed-just-begin.pem1
-rw-r--r--tests/auto/network/ssl/qsslcertificate/more-certificates/natwest-banking.pem36
-rw-r--r--tests/auto/network/ssl/qsslcertificate/more-certificates/no-ending-newline.pem13
-rw-r--r--tests/auto/network/ssl/qsslcertificate/more-certificates/test-cn-two-cns-cert.pem67
-rw-r--r--tests/auto/network/ssl/qsslcertificate/more-certificates/test-cn-with-drink-cert.pem66
-rw-r--r--tests/auto/network/ssl/qsslcertificate/more-certificates/trailing-whitespace.pem13
-rw-r--r--tests/auto/network/ssl/qsslcertificate/qsslcertificate.pro29
-rw-r--r--tests/auto/network/ssl/qsslcertificate/tst_qsslcertificate.cpp995
-rw-r--r--tests/auto/network/ssl/qsslcertificate/verify-certs/README2
-rw-r--r--tests/auto/network/ssl/qsslcertificate/verify-certs/cacert.pem23
-rw-r--r--tests/auto/network/ssl/qsslcertificate/verify-certs/test-addons-mozilla-org-cert.pem34
-rw-r--r--tests/auto/network/ssl/qsslcertificate/verify-certs/test-intermediate-ca-cert.pem66
-rw-r--r--tests/auto/network/ssl/qsslcertificate/verify-certs/test-intermediate-is-ca-cert.pem53
-rw-r--r--tests/auto/network/ssl/qsslcertificate/verify-certs/test-intermediate-not-ca-cert.pem54
-rw-r--r--tests/auto/network/ssl/qsslcertificate/verify-certs/test-ocsp-good-cert.pem67
-rw-r--r--tests/auto/network/ssl/qsslcipher/.gitignore1
-rw-r--r--tests/auto/network/ssl/qsslcipher/qsslcipher.pro18
-rw-r--r--tests/auto/network/ssl/qsslcipher/tst_qsslcipher.cpp101
-rw-r--r--tests/auto/network/ssl/qsslerror/.gitignore1
-rw-r--r--tests/auto/network/ssl/qsslerror/qsslerror.pro18
-rw-r--r--tests/auto/network/ssl/qsslerror/tst_qsslerror.cpp122
-rw-r--r--tests/auto/network/ssl/qsslkey/.gitignore1
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/dsa-pri-1024.derbin0 -> 447 bytes
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/dsa-pri-1024.pem12
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/dsa-pri-512.derbin0 -> 251 bytes
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/dsa-pri-512.pem8
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/dsa-pri-576.derbin0 -> 275 bytes
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/dsa-pri-576.pem8
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/dsa-pri-960.derbin0 -> 419 bytes
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/dsa-pri-960.pem11
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/dsa-pub-1024.derbin0 -> 442 bytes
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/dsa-pub-1024.pem12
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/dsa-pub-512.derbin0 -> 244 bytes
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/dsa-pub-512.pem8
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/dsa-pub-576.derbin0 -> 268 bytes
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/dsa-pub-576.pem8
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/dsa-pub-960.derbin0 -> 414 bytes
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/dsa-pub-960.pem11
-rwxr-xr-xtests/auto/network/ssl/qsslkey/keys/genkeys.sh82
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/rsa-pri-1023.derbin0 -> 605 bytes
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/rsa-pri-1023.pem15
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/rsa-pri-1024.derbin0 -> 608 bytes
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/rsa-pri-1024.pem15
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/rsa-pri-2048.derbin0 -> 1190 bytes
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/rsa-pri-2048.pem27
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/rsa-pri-40.derbin0 -> 49 bytes
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/rsa-pri-40.pem4
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/rsa-pri-511.derbin0 -> 316 bytes
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/rsa-pri-511.pem9
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/rsa-pri-512.derbin0 -> 320 bytes
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/rsa-pri-512.pem9
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/rsa-pri-999.derbin0 -> 591 bytes
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/rsa-pri-999.pem15
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/rsa-pub-1023.derbin0 -> 161 bytes
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/rsa-pub-1023.pem6
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/rsa-pub-1024.derbin0 -> 162 bytes
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/rsa-pub-1024.pem6
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/rsa-pub-2048.derbin0 -> 294 bytes
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/rsa-pub-2048.pem9
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/rsa-pub-40.derbin0 -> 35 bytes
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/rsa-pub-40.pem3
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/rsa-pub-511.derbin0 -> 93 bytes
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/rsa-pub-511.pem4
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/rsa-pub-512.derbin0 -> 94 bytes
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/rsa-pub-512.pem4
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/rsa-pub-999.derbin0 -> 157 bytes
-rw-r--r--tests/auto/network/ssl/qsslkey/keys/rsa-pub-999.pem6
-rw-r--r--tests/auto/network/ssl/qsslkey/qsslkey.pro32
-rw-r--r--tests/auto/network/ssl/qsslkey/rsa-with-passphrase.pem18
-rw-r--r--tests/auto/network/ssl/qsslkey/rsa-without-passphrase.pem15
-rw-r--r--tests/auto/network/ssl/qsslkey/tst_qsslkey.cpp447
-rw-r--r--tests/auto/network/ssl/qsslsocket/.gitignore1
-rw-r--r--tests/auto/network/ssl/qsslsocket/certs/aspiriniks.ca.crt22
-rw-r--r--tests/auto/network/ssl/qsslsocket/certs/fake-login.live.com.key15
-rw-r--r--tests/auto/network/ssl/qsslsocket/certs/fake-login.live.com.pem19
-rw-r--r--tests/auto/network/ssl/qsslsocket/certs/fluke.cert75
-rw-r--r--tests/auto/network/ssl/qsslsocket/certs/fluke.key15
-rw-r--r--tests/auto/network/ssl/qsslsocket/certs/qt-test-server-cacert.pem17
-rw-r--r--tests/auto/network/ssl/qsslsocket/qsslsocket.pro48
-rw-r--r--tests/auto/network/ssl/qsslsocket/ssl.tar.gzbin0 -> 36299 bytes
-rw-r--r--tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp2097
-rw-r--r--tests/auto/network/ssl/ssl.pro8
280 files changed, 59668 insertions, 0 deletions
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 <QtTest/QtTest>
+#include <QtNetwork/QtNetwork>
+#include "../../../../shared/util.h"
+#include "../../../network-settings.h"
+
+#ifndef QT_NO_BEARERMANAGEMENT
+#include <QtNetwork/qnetworkconfigmanager.h>
+#include <QtNetwork/qnetworkconfiguration.h>
+#include <QtNetwork/qnetworksession.h>
+#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<QNetworkSession> 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<QNetworkRequest::CacheLoadControl>("cacheLoadControl");
+ QTest::addColumn<QString>("url");
+ QTest::addColumn<bool>("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<QNetworkRequest::CacheLoadControl>("cacheLoadControl");
+ QTest::addColumn<QString>("url");
+ QTest::addColumn<bool>("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<QNetworkRequest::CacheLoadControl>("cacheLoadControl");
+ QTest::addColumn<QString>("url");
+ QTest::addColumn<bool>("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<QNetworkRequest::CacheLoadControl>("cacheLoadControl");
+ QTest::addColumn<QString>("url");
+ QTest::addColumn<bool>("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<QByteArray> rawHeaderList = reply->rawHeaderList();
+ QList<QByteArray> 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<QByteArray> rawHeaderList = reply->rawHeaderList();
+ QList<QByteArray> 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:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ <ip>
+ <header length="474">
+ <version value="4"/>
+ <tos precedence="Routine" delay="Normal" throughput="Normal"
+ relibility="Normal" reserved="0"/>
+ <total.length value="461"/>
+ <id value="1"/>
+ <flags reserved="0" df="dont" mf="last"/>
+ <offset value="0"/>
+ <ttl value="255"/>
+ <protocol value="6"/>
+ <checksum value="8707"/>
+ <source address="10.0.0.22"/>
+ <destination address="10.0.0.1"/>
+ <options>
+ <end copied="0" class="0" number="0"/>
+ </options>
+ <padding pad="0"/>
+ </header>
+ <payload>
+ </payload>
+ </ip>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+3.2. Example Datagram
+
+ The following is an example TCPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ <tcp>
+ <tcp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <sequence number="322622954"/>
+ <acknowledgement number="689715995"/>
+ <offset number=""/>
+ <reserved value="0"/>
+ <control syn="1" ack="1"/>
+ <window size="1"/>
+ <urgent pointer="0"/>
+ <checksum value="2988"/>
+ <tcp.options>
+ <tcp.end kind="0"/>
+ </tcp.options>
+ <padding pad="0"/>
+ </tcp.header>
+ <payload>
+ </payload>
+ </tcp>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+4.2. Example Datagram
+
+ The following is an example UDPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ <udp>
+ <udp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <udp.length value="143"/>
+ <checksum value="2988"/>
+ </udp.header>
+ <payload>
+ </payload>
+ </udp>
+
+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 ("<?xml".)
+
+5.2. IEEE 802
+
+ BLOAT is encapsulated in IEEE 802 Networks as in [RFC1042] except
+ that the protocol type code for IPoXML is 0xBEEF.
+
+
+
+
+
+Kennedy Informational [Page 6]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+6. Gatewaying over IP
+
+ In order to facilitate the gradual introduction of BLOAT into the
+ public Internet, BLOAT MAY be encapsulated in IP as in [RFC2003] to
+ gateway between networks that run BLOAT natively on their LANs.
+
+7. DTDs
+
+ The Transport DTDs (7.2. and 7.3.) build on the definitions in the
+ Network DTD (7.1.)
+
+ The DTDs are referenced by their PubidLiteral and SystemLiteral (from
+ [XML]) although it is understood that most IPoXML implementations
+ will not need to pull down the DTD, as it will normally be embedded
+ in the implementation, and presents something of a catch-22 if you
+ need to load part of your network protocol over the network.
+
+7.1. IPoXML DTD
+
+ <!--
+ DTD for IP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ -->
+ <!--
+ DTD data types:
+
+ Digits [0..9]+
+
+ Precedence "NetworkControl | InternetworkControl |
+ CRITIC | FlashOverride | Flash | Immediate |
+ Priority | Routine"
+
+ IP4Addr "dotted-decimal" notation of [RFC1123]
+
+ Class [0..3]
+
+ Sec "Unclassified | Confidential | EFTO | MMMM | PROG |
+ Restricted | Secret | Top Secret | Reserved"
+
+ Compartments [0..65535]
+
+ Handling [0..65535]
+
+ TCC [0..16777216]
+
+ -->
+
+
+
+Kennedy Informational [Page 7]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ENTITY % Digits "CDATA">
+ <!ENTITY % Precedence "CDATA">
+ <!ENTITY % IP4Addr "CDATA">
+ <!ENTITY % Class "CDATA">
+ <!ENTITY % Sec "CDATA">
+ <!ENTITY % Compartments "CDATA">
+ <!ENTITY % Handling "CDATA">
+ <!ENTITY % TCC "CDATA">
+
+ <!ELEMENT ip (header, payload)>
+
+ <!ELEMENT header (version, tos, total.length, id, flags, offset, ttl,
+ protocol, checksum, source, destination, options,
+ padding)>
+ <!-- length of header in 32-bit words -->
+ <!ATTLIST header
+ length %Digits; #REQUIRED>
+
+ <!ELEMENT version EMPTY>
+ <!-- ip version. SHOULD be "4" -->
+ <!ATTLIST version
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT tos EMPTY>
+ <!ATTLIST tos
+ precedence %Precedence; #REQUIRED
+ delay (normal | low) #REQUIRED
+ throughput (normal | high) #REQUIRED
+ relibility (normal | high) #REQUIRED
+ reserved CDATA #FIXED "0">
+
+ <!ELEMENT total.length EMPTY>
+ <!--
+ total length of datagram (header and payload) in octets, MUST be
+ less than 65,535 (and SHOULD be less than 1024 for IPoXML on local
+ ethernets).
+ -->
+ <!ATTLIST total.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT id EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST id
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT flags EMPTY>
+ <!-- df = don't fragment, mf = more fragments -->
+ <!ATTLIST flags
+
+
+
+Kennedy Informational [Page 8]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ reserved CDATA #FIXED "0"
+ df (may|dont) #REQUIRED
+ mf (last|more) #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= offset <= 8192 measured in 8 octet (64-bit) chunks -->
+ <!ATTLIST offset
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT ttl EMPTY>
+ <!-- 0 <= ttl <= 255 -->
+ <!ATTLIST ttl
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT protocol EMPTY>
+ <!-- 0 <= protocol <= 255 (per IANA) -->
+ <!ATTLIST protocol
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT checksum EMPTY>
+ <!-- 0 <= checksum <= 65535 (over header only) -->
+ <!ATTLIST checksum
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT source EMPTY>
+ <!ATTLIST source
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT destination EMPTY>
+ <!ATTLIST destination
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT options ( end | noop | security | loose | strict | record
+ | stream | timestamp )*>
+
+ <!ELEMENT end EMPTY>
+ <!ATTLIST end
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "0">
+
+ <!ELEMENT noop EMPTY>
+ <!ATTLIST noop
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "1">
+
+ <!ELEMENT security EMPTY>
+
+
+
+Kennedy Informational [Page 9]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST security
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "2"
+ length CDATA #FIXED "11"
+ security %Sec; #REQUIRED
+ compartments %Compartments; #REQUIRED
+ handling %Handling; #REQUIRED
+ tcc %TCC; #REQUIRED>
+ <!ELEMENT loose (hop)+>
+ <!ATTLIST loose
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "3"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT hop EMPTY>
+ <!ATTLIST hop
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT strict (hop)+>
+ <!ATTLIST strict
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "9"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT record (hop)+>
+ <!ATTLIST record
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "7"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT stream EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST stream
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "8"
+ length CDATA #FIXED "4"
+ id %Digits; #REQUIRED>
+
+ <!ELEMENT timestamp (tstamp)+>
+ <!-- 0 <= oflw <=15 -->
+
+
+
+Kennedy Informational [Page 10]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST timestamp
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "2"
+ number CDATA #FIXED "4"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED
+ oflw %Digits; #REQUIRED
+ flag (0 | 1 | 3) #REQUIRED>
+
+ <!ELEMENT tstamp EMPTY>
+ <!ATTLIST tstamp
+ time %Digits; #REQUIRED
+ address %IP4Addr; #IMPLIED>
+ <!--
+ padding to bring header to 32-bit boundary.
+ pad MUST be "0"*
+ -->
+ <!ELEMENT padding EMPTY>
+ <!ATTLIST padding
+ pad CDATA #REQUIRED>
+
+ <!-- payload MUST be encoded as base-64 [RFC2045], as modified
+ by section 2.1 of this RFC -->
+ <!ELEMENT payload (CDATA)>
+
+7.2. TCPoXML DTD
+
+ <!--
+ DTD for TCP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ -->
+
+ <!-- the pseudoheader is only included for checksum calculations -->
+ <!ELEMENT tcp (tcp.pseudoheader?, tcp.header, payload)>
+
+ <!ELEMENT tcp.header (src, dest, sequence, acknowledgement, offset,
+ reserved, control, window, checksum, urgent,
+ tcp.options, padding)>
+
+ <!ELEMENT src EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+ <!ATTLIST src
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT dest EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+
+
+
+Kennedy Informational [Page 11]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST dest
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT sequence EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST sequence
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT acknowledgement EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST acknowledgement
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= number <= 255 -->
+ <!ATTLIST offset
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT reserved EMPTY>
+ <!ATTLIST reserved
+ value CDATA #FIXED "0">
+
+ <!ELEMENT control EMPTY>
+ <!ATTLIST control
+ urg (0|1) #IMPLIED
+ ack (0|1) #IMPLIED
+ psh (0|1) #IMPLIED
+ rst (0|1) #IMPLIED
+ syn (0|1) #IMPLIED
+ fin (0|1) #IMPLIED>
+
+ <!ELEMENT window EMPTY>
+ <!-- 0 <= size <= 65,535 -->
+ <!ATTLIST window
+ size %Digits; #REQUIRED>
+
+ <!--
+ checksum as in ip, but with
+ the following pseudo-header added into the tcp element:
+ -->
+ <!ELEMENT tcp.pseudoheader (source, destination, protocol,
+ tcp.length)>
+
+ <!--
+ tcp header + data length in octets. does not include the size of
+
+ the pseudoheader.
+ -->
+
+
+
+Kennedy Informational [Page 12]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ELEMENT tcp.length EMPTY>
+ <!ATTLIST tcp.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT urgent EMPTY>
+ <!-- 0 <= pointer <= 65,535 -->
+ <!ATTLIST urgent
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT tcp.options (tcp.end | tcp.noop | tcp.mss)+>
+
+ <!ELEMENT tcp.end EMPTY>
+ <!ATTLIST tcp.end
+ kind CDATA #FIXED "0">
+
+ <!ELEMENT tcp.noop EMPTY>
+ <!ATTLIST tcp.noop
+ kind CDATA #FIXED "1">
+
+ <!ELEMENT tcp.mss EMPTY>
+ <!ATTLIST tcp.mss
+ kind CDATA #FIXED "2"
+ length CDATA #FIXED "4"
+ size %Digits; #REQUIRED>
+
+7.3. UDPoXML DTD
+
+ <!--
+ DTD for UDP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ -->
+
+ <!ELEMENT udp (udp.pseudoheader?, udp.header, payload)>
+
+ <!ELEMENT udp.header (src, dest, udp.length, checksum)>
+
+ <!ELEMENT udp.pseudoheader (source, destination, protocol,
+ udp.length)>
+
+ <!--
+ udp header + data length in octets. does not include the size of
+ the pseudoheader.
+ -->
+ <!ELEMENT udp.length EMPTY>
+ <!ATTLIST udp.length
+ value %Digits; #REQUIRED>
+
+
+
+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 <QtTest/QtTest>
+
+#include <qcoreapplication.h>
+#include <qfile.h>
+#include <qbuffer.h>
+#include "qftp.h"
+#include <qmap.h>
+#include <time.h>
+#include <stdlib.h>
+#include <QNetworkProxy>
+#include <QNetworkConfiguration>
+#include <qnetworkconfigmanager.h>
+#include <QNetworkSession>
+#include <QtNetwork/private/qnetworksession_p.h>
+
+#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<QNetworkSession> networkSessionExplicit;
+ QSharedPointer<QNetworkSession> networkSessionImplicit;
+#endif
+
+ QList<int> 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<QFtp::Command,CommandResult>::Iterator ResMapIt;
+
+ int done_success;
+ int commandSequence_success;
+
+ qlonglong bytesAvailable_finishedGet;
+ qlonglong bytesAvailable_finished;
+ qlonglong bytesAvailable_done;
+
+ QList<QUrlInfo> 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<bool>("setProxy");
+ QTest::addColumn<int>("proxyType");
+ QTest::addColumn<bool>("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<QNetworkSession>(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<QString>("host");
+ QTest::addColumn<uint>("port");
+ QTest::addColumn<int>("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<QString>("host");
+ QTest::addColumn<uint>("port");
+ QTest::addColumn<QString>("user");
+ QTest::addColumn<QString>("password");
+ QTest::addColumn<int>("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<QString>("host");
+ QTest::addColumn<uint>("port");
+ QTest::addColumn<QString>("user");
+ QTest::addColumn<QString>("password");
+ QTest::addColumn<bool>("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<QString>("host");
+ QTest::addColumn<uint>("port");
+ QTest::addColumn<QString>("user");
+ QTest::addColumn<QString>("password");
+ QTest::addColumn<QString>("dir");
+ QTest::addColumn<int>("success");
+ QTest::addColumn<QStringList>("entryNames"); // ### we should rather use a QList<QUrlInfo> 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<QString>("host");
+ QTest::addColumn<uint>("port");
+ QTest::addColumn<QString>("user");
+ QTest::addColumn<QString>("password");
+ QTest::addColumn<QString>("dir");
+ QTest::addColumn<int>("success");
+ QTest::addColumn<QStringList>("entryNames"); // ### we should rather use a QList<QUrlInfo> 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<QString>("host");
+ QTest::addColumn<uint>("port");
+ QTest::addColumn<QString>("user");
+ QTest::addColumn<QString>("password");
+ QTest::addColumn<QString>("file");
+ QTest::addColumn<int>("success");
+ QTest::addColumn<QByteArray>("res");
+ QTest::addColumn<bool>("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<QString>("host");
+ QTest::addColumn<uint>("port");
+ QTest::addColumn<QString>("user");
+ QTest::addColumn<QString>("password");
+ QTest::addColumn<QString>("file");
+ QTest::addColumn<QByteArray>("fileData");
+ QTest::addColumn<bool>("useIODevice");
+ QTest::addColumn<int>("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<QString>("host");
+ QTest::addColumn<uint>("port");
+ QTest::addColumn<QString>("user");
+ QTest::addColumn<QString>("password");
+ QTest::addColumn<QString>("cdDir");
+ QTest::addColumn<QString>("dirToCreate");
+ QTest::addColumn<int>("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<QString>("host");
+ QTest::addColumn<QString>("user");
+ QTest::addColumn<QString>("password");
+ QTest::addColumn<QString>("cdDir");
+ QTest::addColumn<QString>("oldfile");
+ QTest::addColumn<QString>("newfile");
+ QTest::addColumn<QString>("createFile");
+ QTest::addColumn<QString>("renamedFile");
+ QTest::addColumn<int>("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<FtpCommand>)
+
+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<QList<FtpCommand> >("cmds");
+ QTest::addColumn<int>("success");
+
+ // success data
+ {
+ QList<FtpCommand> cmds;
+ cmds << connectToHost01;
+ QTest::newRow( "simple_ok01" ) << cmds << 1;
+ }
+ {
+ QList<FtpCommand> cmds;
+ cmds << connectToHost01;
+ cmds << login01;
+ QTest::newRow( "simple_ok02" ) << cmds << 1;
+ }
+ {
+ QList<FtpCommand> cmds;
+ cmds << connectToHost01;
+ cmds << login01;
+ cmds << close01;
+ QTest::newRow( "simple_ok03" ) << cmds << 1;
+ }
+ {
+ QList<FtpCommand> cmds;
+ cmds << connectToHost01;
+ cmds << close01;
+ QTest::newRow( "simple_ok04" ) << cmds << 1;
+ }
+ {
+ QList<FtpCommand> cmds;
+ cmds << connectToHost01;
+ cmds << login01;
+ cmds << close01;
+ cmds << connectToHost01;
+ cmds << login02;
+ cmds << close01;
+ QTest::newRow( "connect_twice" ) << cmds << 1;
+ }
+
+ // error data
+ {
+ QList<FtpCommand> cmds;
+ cmds << close01;
+ QTest::newRow( "error01" ) << cmds << 0;
+ }
+ {
+ QList<FtpCommand> cmds;
+ cmds << login01;
+ QTest::newRow( "error02" ) << cmds << 0;
+ }
+ {
+ QList<FtpCommand> cmds;
+ cmds << login01;
+ cmds << close01;
+ QTest::newRow( "error03" ) << cmds << 0;
+ }
+ {
+ QList<FtpCommand> cmds;
+ cmds << connectToHost01;
+ cmds << login01;
+ cmds << close01;
+ cmds << login01;
+ QTest::newRow( "error04" ) << cmds << 0;
+ }
+}
+
+void tst_QFtp::commandSequence()
+{
+ QFETCH( QList<FtpCommand>, cmds );
+
+ ftp = newFtp();
+ QList<FtpCommand>::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<QString>("host");
+ QTest::addColumn<uint>("port");
+ QTest::addColumn<QString>("file");
+ QTest::addColumn<QByteArray>("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<QString>("host");
+ QTest::addColumn<QString>("file");
+ QTest::addColumn<int>("type");
+ QTest::addColumn<qlonglong>("bytesAvailFinishedGet");
+ QTest::addColumn<qlonglong>("bytesAvailFinished");
+ QTest::addColumn<qlonglong>("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<QString>("host");
+ QTest::addColumn<uint>("port");
+ QTest::addColumn<QString>("user");
+ QTest::addColumn<QString>("password");
+ QTest::addColumn<QString>("dir");
+ QTest::addColumn<int>("success");
+ QTest::addColumn<QStringList>("entryNames"); // ### we should rather use a QList<QUrlInfo> 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<QTcpSocket *>(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<QTcpSocket *>(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:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ <ip>
+ <header length="474">
+ <version value="4"/>
+ <tos precedence="Routine" delay="Normal" throughput="Normal"
+ relibility="Normal" reserved="0"/>
+ <total.length value="461"/>
+ <id value="1"/>
+ <flags reserved="0" df="dont" mf="last"/>
+ <offset value="0"/>
+ <ttl value="255"/>
+ <protocol value="6"/>
+ <checksum value="8707"/>
+ <source address="10.0.0.22"/>
+ <destination address="10.0.0.1"/>
+ <options>
+ <end copied="0" class="0" number="0"/>
+ </options>
+ <padding pad="0"/>
+ </header>
+ <payload>
+ </payload>
+ </ip>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+3.2. Example Datagram
+
+ The following is an example TCPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ <tcp>
+ <tcp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <sequence number="322622954"/>
+ <acknowledgement number="689715995"/>
+ <offset number=""/>
+ <reserved value="0"/>
+ <control syn="1" ack="1"/>
+ <window size="1"/>
+ <urgent pointer="0"/>
+ <checksum value="2988"/>
+ <tcp.options>
+ <tcp.end kind="0"/>
+ </tcp.options>
+ <padding pad="0"/>
+ </tcp.header>
+ <payload>
+ </payload>
+ </tcp>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+4.2. Example Datagram
+
+ The following is an example UDPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ <udp>
+ <udp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <udp.length value="143"/>
+ <checksum value="2988"/>
+ </udp.header>
+ <payload>
+ </payload>
+ </udp>
+
+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 ("<?xml".)
+
+5.2. IEEE 802
+
+ BLOAT is encapsulated in IEEE 802 Networks as in [RFC1042] except
+ that the protocol type code for IPoXML is 0xBEEF.
+
+
+
+
+
+Kennedy Informational [Page 6]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+6. Gatewaying over IP
+
+ In order to facilitate the gradual introduction of BLOAT into the
+ public Internet, BLOAT MAY be encapsulated in IP as in [RFC2003] to
+ gateway between networks that run BLOAT natively on their LANs.
+
+7. DTDs
+
+ The Transport DTDs (7.2. and 7.3.) build on the definitions in the
+ Network DTD (7.1.)
+
+ The DTDs are referenced by their PubidLiteral and SystemLiteral (from
+ [XML]) although it is understood that most IPoXML implementations
+ will not need to pull down the DTD, as it will normally be embedded
+ in the implementation, and presents something of a catch-22 if you
+ need to load part of your network protocol over the network.
+
+7.1. IPoXML DTD
+
+ <!--
+ DTD for IP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ -->
+ <!--
+ DTD data types:
+
+ Digits [0..9]+
+
+ Precedence "NetworkControl | InternetworkControl |
+ CRITIC | FlashOverride | Flash | Immediate |
+ Priority | Routine"
+
+ IP4Addr "dotted-decimal" notation of [RFC1123]
+
+ Class [0..3]
+
+ Sec "Unclassified | Confidential | EFTO | MMMM | PROG |
+ Restricted | Secret | Top Secret | Reserved"
+
+ Compartments [0..65535]
+
+ Handling [0..65535]
+
+ TCC [0..16777216]
+
+ -->
+
+
+
+Kennedy Informational [Page 7]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ENTITY % Digits "CDATA">
+ <!ENTITY % Precedence "CDATA">
+ <!ENTITY % IP4Addr "CDATA">
+ <!ENTITY % Class "CDATA">
+ <!ENTITY % Sec "CDATA">
+ <!ENTITY % Compartments "CDATA">
+ <!ENTITY % Handling "CDATA">
+ <!ENTITY % TCC "CDATA">
+
+ <!ELEMENT ip (header, payload)>
+
+ <!ELEMENT header (version, tos, total.length, id, flags, offset, ttl,
+ protocol, checksum, source, destination, options,
+ padding)>
+ <!-- length of header in 32-bit words -->
+ <!ATTLIST header
+ length %Digits; #REQUIRED>
+
+ <!ELEMENT version EMPTY>
+ <!-- ip version. SHOULD be "4" -->
+ <!ATTLIST version
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT tos EMPTY>
+ <!ATTLIST tos
+ precedence %Precedence; #REQUIRED
+ delay (normal | low) #REQUIRED
+ throughput (normal | high) #REQUIRED
+ relibility (normal | high) #REQUIRED
+ reserved CDATA #FIXED "0">
+
+ <!ELEMENT total.length EMPTY>
+ <!--
+ total length of datagram (header and payload) in octets, MUST be
+ less than 65,535 (and SHOULD be less than 1024 for IPoXML on local
+ ethernets).
+ -->
+ <!ATTLIST total.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT id EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST id
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT flags EMPTY>
+ <!-- df = don't fragment, mf = more fragments -->
+ <!ATTLIST flags
+
+
+
+Kennedy Informational [Page 8]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ reserved CDATA #FIXED "0"
+ df (may|dont) #REQUIRED
+ mf (last|more) #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= offset <= 8192 measured in 8 octet (64-bit) chunks -->
+ <!ATTLIST offset
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT ttl EMPTY>
+ <!-- 0 <= ttl <= 255 -->
+ <!ATTLIST ttl
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT protocol EMPTY>
+ <!-- 0 <= protocol <= 255 (per IANA) -->
+ <!ATTLIST protocol
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT checksum EMPTY>
+ <!-- 0 <= checksum <= 65535 (over header only) -->
+ <!ATTLIST checksum
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT source EMPTY>
+ <!ATTLIST source
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT destination EMPTY>
+ <!ATTLIST destination
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT options ( end | noop | security | loose | strict | record
+ | stream | timestamp )*>
+
+ <!ELEMENT end EMPTY>
+ <!ATTLIST end
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "0">
+
+ <!ELEMENT noop EMPTY>
+ <!ATTLIST noop
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "1">
+
+ <!ELEMENT security EMPTY>
+
+
+
+Kennedy Informational [Page 9]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST security
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "2"
+ length CDATA #FIXED "11"
+ security %Sec; #REQUIRED
+ compartments %Compartments; #REQUIRED
+ handling %Handling; #REQUIRED
+ tcc %TCC; #REQUIRED>
+ <!ELEMENT loose (hop)+>
+ <!ATTLIST loose
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "3"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT hop EMPTY>
+ <!ATTLIST hop
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT strict (hop)+>
+ <!ATTLIST strict
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "9"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT record (hop)+>
+ <!ATTLIST record
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "7"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT stream EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST stream
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "8"
+ length CDATA #FIXED "4"
+ id %Digits; #REQUIRED>
+
+ <!ELEMENT timestamp (tstamp)+>
+ <!-- 0 <= oflw <=15 -->
+
+
+
+Kennedy Informational [Page 10]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST timestamp
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "2"
+ number CDATA #FIXED "4"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED
+ oflw %Digits; #REQUIRED
+ flag (0 | 1 | 3) #REQUIRED>
+
+ <!ELEMENT tstamp EMPTY>
+ <!ATTLIST tstamp
+ time %Digits; #REQUIRED
+ address %IP4Addr; #IMPLIED>
+ <!--
+ padding to bring header to 32-bit boundary.
+ pad MUST be "0"*
+ -->
+ <!ELEMENT padding EMPTY>
+ <!ATTLIST padding
+ pad CDATA #REQUIRED>
+
+ <!-- payload MUST be encoded as base-64 [RFC2045], as modified
+ by section 2.1 of this RFC -->
+ <!ELEMENT payload (CDATA)>
+
+7.2. TCPoXML DTD
+
+ <!--
+ DTD for TCP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ -->
+
+ <!-- the pseudoheader is only included for checksum calculations -->
+ <!ELEMENT tcp (tcp.pseudoheader?, tcp.header, payload)>
+
+ <!ELEMENT tcp.header (src, dest, sequence, acknowledgement, offset,
+ reserved, control, window, checksum, urgent,
+ tcp.options, padding)>
+
+ <!ELEMENT src EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+ <!ATTLIST src
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT dest EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+
+
+
+Kennedy Informational [Page 11]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST dest
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT sequence EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST sequence
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT acknowledgement EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST acknowledgement
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= number <= 255 -->
+ <!ATTLIST offset
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT reserved EMPTY>
+ <!ATTLIST reserved
+ value CDATA #FIXED "0">
+
+ <!ELEMENT control EMPTY>
+ <!ATTLIST control
+ urg (0|1) #IMPLIED
+ ack (0|1) #IMPLIED
+ psh (0|1) #IMPLIED
+ rst (0|1) #IMPLIED
+ syn (0|1) #IMPLIED
+ fin (0|1) #IMPLIED>
+
+ <!ELEMENT window EMPTY>
+ <!-- 0 <= size <= 65,535 -->
+ <!ATTLIST window
+ size %Digits; #REQUIRED>
+
+ <!--
+ checksum as in ip, but with
+ the following pseudo-header added into the tcp element:
+ -->
+ <!ELEMENT tcp.pseudoheader (source, destination, protocol,
+ tcp.length)>
+
+ <!--
+ tcp header + data length in octets. does not include the size of
+
+ the pseudoheader.
+ -->
+
+
+
+Kennedy Informational [Page 12]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ELEMENT tcp.length EMPTY>
+ <!ATTLIST tcp.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT urgent EMPTY>
+ <!-- 0 <= pointer <= 65,535 -->
+ <!ATTLIST urgent
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT tcp.options (tcp.end | tcp.noop | tcp.mss)+>
+
+ <!ELEMENT tcp.end EMPTY>
+ <!ATTLIST tcp.end
+ kind CDATA #FIXED "0">
+
+ <!ELEMENT tcp.noop EMPTY>
+ <!ATTLIST tcp.noop
+ kind CDATA #FIXED "1">
+
+ <!ELEMENT tcp.mss EMPTY>
+ <!ATTLIST tcp.mss
+ kind CDATA #FIXED "2"
+ length CDATA #FIXED "4"
+ size %Digits; #REQUIRED>
+
+7.3. UDPoXML DTD
+
+ <!--
+ DTD for UDP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ -->
+
+ <!ELEMENT udp (udp.pseudoheader?, udp.header, payload)>
+
+ <!ELEMENT udp.header (src, dest, udp.length, checksum)>
+
+ <!ELEMENT udp.pseudoheader (source, destination, protocol,
+ udp.length)>
+
+ <!--
+ udp header + data length in octets. does not include the size of
+ the pseudoheader.
+ -->
+ <!ELEMENT udp.length EMPTY>
+ <!ATTLIST udp.length
+ value %Digits; #REQUIRED>
+
+
+
+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 @@
+<html>
+ <head>
+ <title>Test</title>
+ </head>
+ <body>
+ <h1>Test</h1>
+ </body>
+</html>
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 <QtTest/QtTest>
+
+#include <qbuffer.h>
+#include <qcoreapplication.h>
+#include <qfile.h>
+#include <qhostinfo.h>
+#include <qhttp.h>
+#include <qlist.h>
+#include <qpointer.h>
+#include <qtcpsocket.h>
+#include <qtcpserver.h>
+#include <qauthenticator.h>
+#include <QNetworkProxy>
+#ifndef QT_NO_OPENSSL
+# include <qsslsocket.h>
+#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<int,RequestResult>::Iterator ResMapIt;
+ QList<int> 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<bool>("setProxy");
+ QTest::addColumn<int>("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<QString>("host");
+ QTest::addColumn<uint>("port");
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<int>("success");
+ QTest::addColumn<int>("statusCode");
+ QTest::addColumn<QByteArray>("res");
+ QTest::addColumn<bool>("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<QString>("host");
+ QTest::addColumn<uint>("port");
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<int>("success");
+ QTest::addColumn<int>("statusCode");
+ QTest::addColumn<uint>("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<QString>("source");
+ QTest::addColumn<bool>("useIODevice");
+ QTest::addColumn<bool>("useProxy");
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<int>("port");
+ QTest::addColumn<bool>("ssl");
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<QByteArray>("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<QSslError> &)),
+ 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<QString>("source");
+ QTest::addColumn<bool>("useIODevice");
+ QTest::addColumn<bool>("useProxy");
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<int>("port");
+ QTest::addColumn<QString>("method");
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<QByteArray>("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<QString>("host");
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<QString>("user");
+ QTest::addColumn<QString>("pass");
+ QTest::addColumn<int>("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<QString>("proxyhost");
+ QTest::addColumn<int>("port");
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<QString>("proxyuser");
+ QTest::addColumn<QString>("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<QHttp *>(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<QTcpSocket> 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<QSslError>)),
+ &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<QHttp *>(sender());
+ if (http)
+ http->abort();
+}
+
+void tst_QHttp::abortInReadyRead()
+{
+ QHttp http;
+ http.setHost(QtNetworkSettings::serverName());
+ http.get("/qtest/bigfile");
+
+ qRegisterMetaType<QHttpResponseHeader>();
+ 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<QHttpResponseHeader>();
+ 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:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ <ip>
+ <header length="474">
+ <version value="4"/>
+ <tos precedence="Routine" delay="Normal" throughput="Normal"
+ relibility="Normal" reserved="0"/>
+ <total.length value="461"/>
+ <id value="1"/>
+ <flags reserved="0" df="dont" mf="last"/>
+ <offset value="0"/>
+ <ttl value="255"/>
+ <protocol value="6"/>
+ <checksum value="8707"/>
+ <source address="10.0.0.22"/>
+ <destination address="10.0.0.1"/>
+ <options>
+ <end copied="0" class="0" number="0"/>
+ </options>
+ <padding pad="0"/>
+ </header>
+ <payload>
+ </payload>
+ </ip>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+3.2. Example Datagram
+
+ The following is an example TCPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ <tcp>
+ <tcp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <sequence number="322622954"/>
+ <acknowledgement number="689715995"/>
+ <offset number=""/>
+ <reserved value="0"/>
+ <control syn="1" ack="1"/>
+ <window size="1"/>
+ <urgent pointer="0"/>
+ <checksum value="2988"/>
+ <tcp.options>
+ <tcp.end kind="0"/>
+ </tcp.options>
+ <padding pad="0"/>
+ </tcp.header>
+ <payload>
+ </payload>
+ </tcp>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+4.2. Example Datagram
+
+ The following is an example UDPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ <udp>
+ <udp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <udp.length value="143"/>
+ <checksum value="2988"/>
+ </udp.header>
+ <payload>
+ </payload>
+ </udp>
+
+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 ("<?xml".)
+
+5.2. IEEE 802
+
+ BLOAT is encapsulated in IEEE 802 Networks as in [RFC1042] except
+ that the protocol type code for IPoXML is 0xBEEF.
+
+
+
+
+
+Kennedy Informational [Page 6]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+6. Gatewaying over IP
+
+ In order to facilitate the gradual introduction of BLOAT into the
+ public Internet, BLOAT MAY be encapsulated in IP as in [RFC2003] to
+ gateway between networks that run BLOAT natively on their LANs.
+
+7. DTDs
+
+ The Transport DTDs (7.2. and 7.3.) build on the definitions in the
+ Network DTD (7.1.)
+
+ The DTDs are referenced by their PubidLiteral and SystemLiteral (from
+ [XML]) although it is understood that most IPoXML implementations
+ will not need to pull down the DTD, as it will normally be embedded
+ in the implementation, and presents something of a catch-22 if you
+ need to load part of your network protocol over the network.
+
+7.1. IPoXML DTD
+
+ <!--
+ DTD for IP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ -->
+ <!--
+ DTD data types:
+
+ Digits [0..9]+
+
+ Precedence "NetworkControl | InternetworkControl |
+ CRITIC | FlashOverride | Flash | Immediate |
+ Priority | Routine"
+
+ IP4Addr "dotted-decimal" notation of [RFC1123]
+
+ Class [0..3]
+
+ Sec "Unclassified | Confidential | EFTO | MMMM | PROG |
+ Restricted | Secret | Top Secret | Reserved"
+
+ Compartments [0..65535]
+
+ Handling [0..65535]
+
+ TCC [0..16777216]
+
+ -->
+
+
+
+Kennedy Informational [Page 7]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ENTITY % Digits "CDATA">
+ <!ENTITY % Precedence "CDATA">
+ <!ENTITY % IP4Addr "CDATA">
+ <!ENTITY % Class "CDATA">
+ <!ENTITY % Sec "CDATA">
+ <!ENTITY % Compartments "CDATA">
+ <!ENTITY % Handling "CDATA">
+ <!ENTITY % TCC "CDATA">
+
+ <!ELEMENT ip (header, payload)>
+
+ <!ELEMENT header (version, tos, total.length, id, flags, offset, ttl,
+ protocol, checksum, source, destination, options,
+ padding)>
+ <!-- length of header in 32-bit words -->
+ <!ATTLIST header
+ length %Digits; #REQUIRED>
+
+ <!ELEMENT version EMPTY>
+ <!-- ip version. SHOULD be "4" -->
+ <!ATTLIST version
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT tos EMPTY>
+ <!ATTLIST tos
+ precedence %Precedence; #REQUIRED
+ delay (normal | low) #REQUIRED
+ throughput (normal | high) #REQUIRED
+ relibility (normal | high) #REQUIRED
+ reserved CDATA #FIXED "0">
+
+ <!ELEMENT total.length EMPTY>
+ <!--
+ total length of datagram (header and payload) in octets, MUST be
+ less than 65,535 (and SHOULD be less than 1024 for IPoXML on local
+ ethernets).
+ -->
+ <!ATTLIST total.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT id EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST id
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT flags EMPTY>
+ <!-- df = don't fragment, mf = more fragments -->
+ <!ATTLIST flags
+
+
+
+Kennedy Informational [Page 8]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ reserved CDATA #FIXED "0"
+ df (may|dont) #REQUIRED
+ mf (last|more) #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= offset <= 8192 measured in 8 octet (64-bit) chunks -->
+ <!ATTLIST offset
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT ttl EMPTY>
+ <!-- 0 <= ttl <= 255 -->
+ <!ATTLIST ttl
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT protocol EMPTY>
+ <!-- 0 <= protocol <= 255 (per IANA) -->
+ <!ATTLIST protocol
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT checksum EMPTY>
+ <!-- 0 <= checksum <= 65535 (over header only) -->
+ <!ATTLIST checksum
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT source EMPTY>
+ <!ATTLIST source
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT destination EMPTY>
+ <!ATTLIST destination
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT options ( end | noop | security | loose | strict | record
+ | stream | timestamp )*>
+
+ <!ELEMENT end EMPTY>
+ <!ATTLIST end
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "0">
+
+ <!ELEMENT noop EMPTY>
+ <!ATTLIST noop
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "1">
+
+ <!ELEMENT security EMPTY>
+
+
+
+Kennedy Informational [Page 9]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST security
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "2"
+ length CDATA #FIXED "11"
+ security %Sec; #REQUIRED
+ compartments %Compartments; #REQUIRED
+ handling %Handling; #REQUIRED
+ tcc %TCC; #REQUIRED>
+ <!ELEMENT loose (hop)+>
+ <!ATTLIST loose
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "3"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT hop EMPTY>
+ <!ATTLIST hop
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT strict (hop)+>
+ <!ATTLIST strict
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "9"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT record (hop)+>
+ <!ATTLIST record
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "7"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT stream EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST stream
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "8"
+ length CDATA #FIXED "4"
+ id %Digits; #REQUIRED>
+
+ <!ELEMENT timestamp (tstamp)+>
+ <!-- 0 <= oflw <=15 -->
+
+
+
+Kennedy Informational [Page 10]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST timestamp
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "2"
+ number CDATA #FIXED "4"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED
+ oflw %Digits; #REQUIRED
+ flag (0 | 1 | 3) #REQUIRED>
+
+ <!ELEMENT tstamp EMPTY>
+ <!ATTLIST tstamp
+ time %Digits; #REQUIRED
+ address %IP4Addr; #IMPLIED>
+ <!--
+ padding to bring header to 32-bit boundary.
+ pad MUST be "0"*
+ -->
+ <!ELEMENT padding EMPTY>
+ <!ATTLIST padding
+ pad CDATA #REQUIRED>
+
+ <!-- payload MUST be encoded as base-64 [RFC2045], as modified
+ by section 2.1 of this RFC -->
+ <!ELEMENT payload (CDATA)>
+
+7.2. TCPoXML DTD
+
+ <!--
+ DTD for TCP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ -->
+
+ <!-- the pseudoheader is only included for checksum calculations -->
+ <!ELEMENT tcp (tcp.pseudoheader?, tcp.header, payload)>
+
+ <!ELEMENT tcp.header (src, dest, sequence, acknowledgement, offset,
+ reserved, control, window, checksum, urgent,
+ tcp.options, padding)>
+
+ <!ELEMENT src EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+ <!ATTLIST src
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT dest EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+
+
+
+Kennedy Informational [Page 11]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST dest
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT sequence EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST sequence
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT acknowledgement EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST acknowledgement
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= number <= 255 -->
+ <!ATTLIST offset
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT reserved EMPTY>
+ <!ATTLIST reserved
+ value CDATA #FIXED "0">
+
+ <!ELEMENT control EMPTY>
+ <!ATTLIST control
+ urg (0|1) #IMPLIED
+ ack (0|1) #IMPLIED
+ psh (0|1) #IMPLIED
+ rst (0|1) #IMPLIED
+ syn (0|1) #IMPLIED
+ fin (0|1) #IMPLIED>
+
+ <!ELEMENT window EMPTY>
+ <!-- 0 <= size <= 65,535 -->
+ <!ATTLIST window
+ size %Digits; #REQUIRED>
+
+ <!--
+ checksum as in ip, but with
+ the following pseudo-header added into the tcp element:
+ -->
+ <!ELEMENT tcp.pseudoheader (source, destination, protocol,
+ tcp.length)>
+
+ <!--
+ tcp header + data length in octets. does not include the size of
+
+ the pseudoheader.
+ -->
+
+
+
+Kennedy Informational [Page 12]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ELEMENT tcp.length EMPTY>
+ <!ATTLIST tcp.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT urgent EMPTY>
+ <!-- 0 <= pointer <= 65,535 -->
+ <!ATTLIST urgent
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT tcp.options (tcp.end | tcp.noop | tcp.mss)+>
+
+ <!ELEMENT tcp.end EMPTY>
+ <!ATTLIST tcp.end
+ kind CDATA #FIXED "0">
+
+ <!ELEMENT tcp.noop EMPTY>
+ <!ATTLIST tcp.noop
+ kind CDATA #FIXED "1">
+
+ <!ELEMENT tcp.mss EMPTY>
+ <!ATTLIST tcp.mss
+ kind CDATA #FIXED "2"
+ length CDATA #FIXED "4"
+ size %Digits; #REQUIRED>
+
+7.3. UDPoXML DTD
+
+ <!--
+ DTD for UDP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ -->
+
+ <!ELEMENT udp (udp.pseudoheader?, udp.header, payload)>
+
+ <!ELEMENT udp.header (src, dest, udp.length, checksum)>
+
+ <!ELEMENT udp.pseudoheader (source, destination, protocol,
+ udp.length)>
+
+ <!--
+ udp header + data length in octets. does not include the size of
+ the pseudoheader.
+ -->
+ <!ELEMENT udp.length EMPTY>
+ <!ATTLIST udp.length
+ value %Digits; #REQUIRED>
+
+
+
+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:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ <ip>
+ <header length="474">
+ <version value="4"/>
+ <tos precedence="Routine" delay="Normal" throughput="Normal"
+ relibility="Normal" reserved="0"/>
+ <total.length value="461"/>
+ <id value="1"/>
+ <flags reserved="0" df="dont" mf="last"/>
+ <offset value="0"/>
+ <ttl value="255"/>
+ <protocol value="6"/>
+ <checksum value="8707"/>
+ <source address="10.0.0.22"/>
+ <destination address="10.0.0.1"/>
+ <options>
+ <end copied="0" class="0" number="0"/>
+ </options>
+ <padding pad="0"/>
+ </header>
+ <payload>
+ </payload>
+ </ip>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+3.2. Example Datagram
+
+ The following is an example TCPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ <tcp>
+ <tcp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <sequence number="322622954"/>
+ <acknowledgement number="689715995"/>
+ <offset number=""/>
+ <reserved value="0"/>
+ <control syn="1" ack="1"/>
+ <window size="1"/>
+ <urgent pointer="0"/>
+ <checksum value="2988"/>
+ <tcp.options>
+ <tcp.end kind="0"/>
+ </tcp.options>
+ <padding pad="0"/>
+ </tcp.header>
+ <payload>
+ </payload>
+ </tcp>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+4.2. Example Datagram
+
+ The following is an example UDPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ <udp>
+ <udp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <udp.length value="143"/>
+ <checksum value="2988"/>
+ </udp.header>
+ <payload>
+ </payload>
+ </udp>
+
+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 ("<?xml".)
+
+5.2. IEEE 802
+
+ BLOAT is encapsulated in IEEE 802 Networks as in [RFC1042] except
+ that the protocol type code for IPoXML is 0xBEEF.
+
+
+
+
+
+Kennedy Informational [Page 6]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+6. Gatewaying over IP
+
+ In order to facilitate the gradual introduction of BLOAT into the
+ public Internet, BLOAT MAY be encapsulated in IP as in [RFC2003] to
+ gateway between networks that run BLOAT natively on their LANs.
+
+7. DTDs
+
+ The Transport DTDs (7.2. and 7.3.) build on the definitions in the
+ Network DTD (7.1.)
+
+ The DTDs are referenced by their PubidLiteral and SystemLiteral (from
+ [XML]) although it is understood that most IPoXML implementations
+ will not need to pull down the DTD, as it will normally be embedded
+ in the implementation, and presents something of a catch-22 if you
+ need to load part of your network protocol over the network.
+
+7.1. IPoXML DTD
+
+ <!--
+ DTD for IP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ -->
+ <!--
+ DTD data types:
+
+ Digits [0..9]+
+
+ Precedence "NetworkControl | InternetworkControl |
+ CRITIC | FlashOverride | Flash | Immediate |
+ Priority | Routine"
+
+ IP4Addr "dotted-decimal" notation of [RFC1123]
+
+ Class [0..3]
+
+ Sec "Unclassified | Confidential | EFTO | MMMM | PROG |
+ Restricted | Secret | Top Secret | Reserved"
+
+ Compartments [0..65535]
+
+ Handling [0..65535]
+
+ TCC [0..16777216]
+
+ -->
+
+
+
+Kennedy Informational [Page 7]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ENTITY % Digits "CDATA">
+ <!ENTITY % Precedence "CDATA">
+ <!ENTITY % IP4Addr "CDATA">
+ <!ENTITY % Class "CDATA">
+ <!ENTITY % Sec "CDATA">
+ <!ENTITY % Compartments "CDATA">
+ <!ENTITY % Handling "CDATA">
+ <!ENTITY % TCC "CDATA">
+
+ <!ELEMENT ip (header, payload)>
+
+ <!ELEMENT header (version, tos, total.length, id, flags, offset, ttl,
+ protocol, checksum, source, destination, options,
+ padding)>
+ <!-- length of header in 32-bit words -->
+ <!ATTLIST header
+ length %Digits; #REQUIRED>
+
+ <!ELEMENT version EMPTY>
+ <!-- ip version. SHOULD be "4" -->
+ <!ATTLIST version
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT tos EMPTY>
+ <!ATTLIST tos
+ precedence %Precedence; #REQUIRED
+ delay (normal | low) #REQUIRED
+ throughput (normal | high) #REQUIRED
+ relibility (normal | high) #REQUIRED
+ reserved CDATA #FIXED "0">
+
+ <!ELEMENT total.length EMPTY>
+ <!--
+ total length of datagram (header and payload) in octets, MUST be
+ less than 65,535 (and SHOULD be less than 1024 for IPoXML on local
+ ethernets).
+ -->
+ <!ATTLIST total.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT id EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST id
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT flags EMPTY>
+ <!-- df = don't fragment, mf = more fragments -->
+ <!ATTLIST flags
+
+
+
+Kennedy Informational [Page 8]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ reserved CDATA #FIXED "0"
+ df (may|dont) #REQUIRED
+ mf (last|more) #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= offset <= 8192 measured in 8 octet (64-bit) chunks -->
+ <!ATTLIST offset
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT ttl EMPTY>
+ <!-- 0 <= ttl <= 255 -->
+ <!ATTLIST ttl
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT protocol EMPTY>
+ <!-- 0 <= protocol <= 255 (per IANA) -->
+ <!ATTLIST protocol
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT checksum EMPTY>
+ <!-- 0 <= checksum <= 65535 (over header only) -->
+ <!ATTLIST checksum
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT source EMPTY>
+ <!ATTLIST source
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT destination EMPTY>
+ <!ATTLIST destination
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT options ( end | noop | security | loose | strict | record
+ | stream | timestamp )*>
+
+ <!ELEMENT end EMPTY>
+ <!ATTLIST end
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "0">
+
+ <!ELEMENT noop EMPTY>
+ <!ATTLIST noop
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "1">
+
+ <!ELEMENT security EMPTY>
+
+
+
+Kennedy Informational [Page 9]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST security
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "2"
+ length CDATA #FIXED "11"
+ security %Sec; #REQUIRED
+ compartments %Compartments; #REQUIRED
+ handling %Handling; #REQUIRED
+ tcc %TCC; #REQUIRED>
+ <!ELEMENT loose (hop)+>
+ <!ATTLIST loose
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "3"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT hop EMPTY>
+ <!ATTLIST hop
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT strict (hop)+>
+ <!ATTLIST strict
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "9"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT record (hop)+>
+ <!ATTLIST record
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "7"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT stream EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST stream
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "8"
+ length CDATA #FIXED "4"
+ id %Digits; #REQUIRED>
+
+ <!ELEMENT timestamp (tstamp)+>
+ <!-- 0 <= oflw <=15 -->
+
+
+
+Kennedy Informational [Page 10]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST timestamp
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "2"
+ number CDATA #FIXED "4"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED
+ oflw %Digits; #REQUIRED
+ flag (0 | 1 | 3) #REQUIRED>
+
+ <!ELEMENT tstamp EMPTY>
+ <!ATTLIST tstamp
+ time %Digits; #REQUIRED
+ address %IP4Addr; #IMPLIED>
+ <!--
+ padding to bring header to 32-bit boundary.
+ pad MUST be "0"*
+ -->
+ <!ELEMENT padding EMPTY>
+ <!ATTLIST padding
+ pad CDATA #REQUIRED>
+
+ <!-- payload MUST be encoded as base-64 [RFC2045], as modified
+ by section 2.1 of this RFC -->
+ <!ELEMENT payload (CDATA)>
+
+7.2. TCPoXML DTD
+
+ <!--
+ DTD for TCP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ -->
+
+ <!-- the pseudoheader is only included for checksum calculations -->
+ <!ELEMENT tcp (tcp.pseudoheader?, tcp.header, payload)>
+
+ <!ELEMENT tcp.header (src, dest, sequence, acknowledgement, offset,
+ reserved, control, window, checksum, urgent,
+ tcp.options, padding)>
+
+ <!ELEMENT src EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+ <!ATTLIST src
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT dest EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+
+
+
+Kennedy Informational [Page 11]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST dest
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT sequence EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST sequence
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT acknowledgement EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST acknowledgement
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= number <= 255 -->
+ <!ATTLIST offset
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT reserved EMPTY>
+ <!ATTLIST reserved
+ value CDATA #FIXED "0">
+
+ <!ELEMENT control EMPTY>
+ <!ATTLIST control
+ urg (0|1) #IMPLIED
+ ack (0|1) #IMPLIED
+ psh (0|1) #IMPLIED
+ rst (0|1) #IMPLIED
+ syn (0|1) #IMPLIED
+ fin (0|1) #IMPLIED>
+
+ <!ELEMENT window EMPTY>
+ <!-- 0 <= size <= 65,535 -->
+ <!ATTLIST window
+ size %Digits; #REQUIRED>
+
+ <!--
+ checksum as in ip, but with
+ the following pseudo-header added into the tcp element:
+ -->
+ <!ELEMENT tcp.pseudoheader (source, destination, protocol,
+ tcp.length)>
+
+ <!--
+ tcp header + data length in octets. does not include the size of
+
+ the pseudoheader.
+ -->
+
+
+
+Kennedy Informational [Page 12]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ELEMENT tcp.length EMPTY>
+ <!ATTLIST tcp.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT urgent EMPTY>
+ <!-- 0 <= pointer <= 65,535 -->
+ <!ATTLIST urgent
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT tcp.options (tcp.end | tcp.noop | tcp.mss)+>
+
+ <!ELEMENT tcp.end EMPTY>
+ <!ATTLIST tcp.end
+ kind CDATA #FIXED "0">
+
+ <!ELEMENT tcp.noop EMPTY>
+ <!ATTLIST tcp.noop
+ kind CDATA #FIXED "1">
+
+ <!ELEMENT tcp.mss EMPTY>
+ <!ATTLIST tcp.mss
+ kind CDATA #FIXED "2"
+ length CDATA #FIXED "4"
+ size %Digits; #REQUIRED>
+
+7.3. UDPoXML DTD
+
+ <!--
+ DTD for UDP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ -->
+
+ <!ELEMENT udp (udp.pseudoheader?, udp.header, payload)>
+
+ <!ELEMENT udp.header (src, dest, udp.length, checksum)>
+
+ <!ELEMENT udp.pseudoheader (source, destination, protocol,
+ udp.length)>
+
+ <!--
+ udp header + data length in octets. does not include the size of
+ the pseudoheader.
+ -->
+ <!ELEMENT udp.length EMPTY>
+ <!ATTLIST udp.length
+ value %Digits; #REQUIRED>
+
+
+
+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:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ <ip>
+ <header length="474">
+ <version value="4"/>
+ <tos precedence="Routine" delay="Normal" throughput="Normal"
+ relibility="Normal" reserved="0"/>
+ <total.length value="461"/>
+ <id value="1"/>
+ <flags reserved="0" df="dont" mf="last"/>
+ <offset value="0"/>
+ <ttl value="255"/>
+ <protocol value="6"/>
+ <checksum value="8707"/>
+ <source address="10.0.0.22"/>
+ <destination address="10.0.0.1"/>
+ <options>
+ <end copied="0" class="0" number="0"/>
+ </options>
+ <padding pad="0"/>
+ </header>
+ <payload>
+ </payload>
+ </ip>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+3.2. Example Datagram
+
+ The following is an example TCPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ <tcp>
+ <tcp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <sequence number="322622954"/>
+ <acknowledgement number="689715995"/>
+ <offset number=""/>
+ <reserved value="0"/>
+ <control syn="1" ack="1"/>
+ <window size="1"/>
+ <urgent pointer="0"/>
+ <checksum value="2988"/>
+ <tcp.options>
+ <tcp.end kind="0"/>
+ </tcp.options>
+ <padding pad="0"/>
+ </tcp.header>
+ <payload>
+ </payload>
+ </tcp>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+4.2. Example Datagram
+
+ The following is an example UDPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ <udp>
+ <udp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <udp.length value="143"/>
+ <checksum value="2988"/>
+ </udp.header>
+ <payload>
+ </payload>
+ </udp>
+
+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 ("<?xml".)
+
+5.2. IEEE 802
+
+ BLOAT is encapsulated in IEEE 802 Networks as in [RFC1042] except
+ that the protocol type code for IPoXML is 0xBEEF.
+
+
+
+
+
+Kennedy Informational [Page 6]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+6. Gatewaying over IP
+
+ In order to facilitate the gradual introduction of BLOAT into the
+ public Internet, BLOAT MAY be encapsulated in IP as in [RFC2003] to
+ gateway between networks that run BLOAT natively on their LANs.
+
+7. DTDs
+
+ The Transport DTDs (7.2. and 7.3.) build on the definitions in the
+ Network DTD (7.1.)
+
+ The DTDs are referenced by their PubidLiteral and SystemLiteral (from
+ [XML]) although it is understood that most IPoXML implementations
+ will not need to pull down the DTD, as it will normally be embedded
+ in the implementation, and presents something of a catch-22 if you
+ need to load part of your network protocol over the network.
+
+7.1. IPoXML DTD
+
+ <!--
+ DTD for IP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ -->
+ <!--
+ DTD data types:
+
+ Digits [0..9]+
+
+ Precedence "NetworkControl | InternetworkControl |
+ CRITIC | FlashOverride | Flash | Immediate |
+ Priority | Routine"
+
+ IP4Addr "dotted-decimal" notation of [RFC1123]
+
+ Class [0..3]
+
+ Sec "Unclassified | Confidential | EFTO | MMMM | PROG |
+ Restricted | Secret | Top Secret | Reserved"
+
+ Compartments [0..65535]
+
+ Handling [0..65535]
+
+ TCC [0..16777216]
+
+ -->
+
+
+
+Kennedy Informational [Page 7]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ENTITY % Digits "CDATA">
+ <!ENTITY % Precedence "CDATA">
+ <!ENTITY % IP4Addr "CDATA">
+ <!ENTITY % Class "CDATA">
+ <!ENTITY % Sec "CDATA">
+ <!ENTITY % Compartments "CDATA">
+ <!ENTITY % Handling "CDATA">
+ <!ENTITY % TCC "CDATA">
+
+ <!ELEMENT ip (header, payload)>
+
+ <!ELEMENT header (version, tos, total.length, id, flags, offset, ttl,
+ protocol, checksum, source, destination, options,
+ padding)>
+ <!-- length of header in 32-bit words -->
+ <!ATTLIST header
+ length %Digits; #REQUIRED>
+
+ <!ELEMENT version EMPTY>
+ <!-- ip version. SHOULD be "4" -->
+ <!ATTLIST version
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT tos EMPTY>
+ <!ATTLIST tos
+ precedence %Precedence; #REQUIRED
+ delay (normal | low) #REQUIRED
+ throughput (normal | high) #REQUIRED
+ relibility (normal | high) #REQUIRED
+ reserved CDATA #FIXED "0">
+
+ <!ELEMENT total.length EMPTY>
+ <!--
+ total length of datagram (header and payload) in octets, MUST be
+ less than 65,535 (and SHOULD be less than 1024 for IPoXML on local
+ ethernets).
+ -->
+ <!ATTLIST total.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT id EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST id
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT flags EMPTY>
+ <!-- df = don't fragment, mf = more fragments -->
+ <!ATTLIST flags
+
+
+
+Kennedy Informational [Page 8]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ reserved CDATA #FIXED "0"
+ df (may|dont) #REQUIRED
+ mf (last|more) #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= offset <= 8192 measured in 8 octet (64-bit) chunks -->
+ <!ATTLIST offset
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT ttl EMPTY>
+ <!-- 0 <= ttl <= 255 -->
+ <!ATTLIST ttl
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT protocol EMPTY>
+ <!-- 0 <= protocol <= 255 (per IANA) -->
+ <!ATTLIST protocol
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT checksum EMPTY>
+ <!-- 0 <= checksum <= 65535 (over header only) -->
+ <!ATTLIST checksum
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT source EMPTY>
+ <!ATTLIST source
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT destination EMPTY>
+ <!ATTLIST destination
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT options ( end | noop | security | loose | strict | record
+ | stream | timestamp )*>
+
+ <!ELEMENT end EMPTY>
+ <!ATTLIST end
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "0">
+
+ <!ELEMENT noop EMPTY>
+ <!ATTLIST noop
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "1">
+
+ <!ELEMENT security EMPTY>
+
+
+
+Kennedy Informational [Page 9]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST security
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "2"
+ length CDATA #FIXED "11"
+ security %Sec; #REQUIRED
+ compartments %Compartments; #REQUIRED
+ handling %Handling; #REQUIRED
+ tcc %TCC; #REQUIRED>
+ <!ELEMENT loose (hop)+>
+ <!ATTLIST loose
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "3"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT hop EMPTY>
+ <!ATTLIST hop
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT strict (hop)+>
+ <!ATTLIST strict
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "9"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT record (hop)+>
+ <!ATTLIST record
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "7"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT stream EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST stream
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "8"
+ length CDATA #FIXED "4"
+ id %Digits; #REQUIRED>
+
+ <!ELEMENT timestamp (tstamp)+>
+ <!-- 0 <= oflw <=15 -->
+
+
+
+Kennedy Informational [Page 10]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST timestamp
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "2"
+ number CDATA #FIXED "4"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED
+ oflw %Digits; #REQUIRED
+ flag (0 | 1 | 3) #REQUIRED>
+
+ <!ELEMENT tstamp EMPTY>
+ <!ATTLIST tstamp
+ time %Digits; #REQUIRED
+ address %IP4Addr; #IMPLIED>
+ <!--
+ padding to bring header to 32-bit boundary.
+ pad MUST be "0"*
+ -->
+ <!ELEMENT padding EMPTY>
+ <!ATTLIST padding
+ pad CDATA #REQUIRED>
+
+ <!-- payload MUST be encoded as base-64 [RFC2045], as modified
+ by section 2.1 of this RFC -->
+ <!ELEMENT payload (CDATA)>
+
+7.2. TCPoXML DTD
+
+ <!--
+ DTD for TCP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ -->
+
+ <!-- the pseudoheader is only included for checksum calculations -->
+ <!ELEMENT tcp (tcp.pseudoheader?, tcp.header, payload)>
+
+ <!ELEMENT tcp.header (src, dest, sequence, acknowledgement, offset,
+ reserved, control, window, checksum, urgent,
+ tcp.options, padding)>
+
+ <!ELEMENT src EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+ <!ATTLIST src
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT dest EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+
+
+
+Kennedy Informational [Page 11]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST dest
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT sequence EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST sequence
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT acknowledgement EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST acknowledgement
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= number <= 255 -->
+ <!ATTLIST offset
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT reserved EMPTY>
+ <!ATTLIST reserved
+ value CDATA #FIXED "0">
+
+ <!ELEMENT control EMPTY>
+ <!ATTLIST control
+ urg (0|1) #IMPLIED
+ ack (0|1) #IMPLIED
+ psh (0|1) #IMPLIED
+ rst (0|1) #IMPLIED
+ syn (0|1) #IMPLIED
+ fin (0|1) #IMPLIED>
+
+ <!ELEMENT window EMPTY>
+ <!-- 0 <= size <= 65,535 -->
+ <!ATTLIST window
+ size %Digits; #REQUIRED>
+
+ <!--
+ checksum as in ip, but with
+ the following pseudo-header added into the tcp element:
+ -->
+ <!ELEMENT tcp.pseudoheader (source, destination, protocol,
+ tcp.length)>
+
+ <!--
+ tcp header + data length in octets. does not include the size of
+
+ the pseudoheader.
+ -->
+
+
+
+Kennedy Informational [Page 12]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ELEMENT tcp.length EMPTY>
+ <!ATTLIST tcp.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT urgent EMPTY>
+ <!-- 0 <= pointer <= 65,535 -->
+ <!ATTLIST urgent
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT tcp.options (tcp.end | tcp.noop | tcp.mss)+>
+
+ <!ELEMENT tcp.end EMPTY>
+ <!ATTLIST tcp.end
+ kind CDATA #FIXED "0">
+
+ <!ELEMENT tcp.noop EMPTY>
+ <!ATTLIST tcp.noop
+ kind CDATA #FIXED "1">
+
+ <!ELEMENT tcp.mss EMPTY>
+ <!ATTLIST tcp.mss
+ kind CDATA #FIXED "2"
+ length CDATA #FIXED "4"
+ size %Digits; #REQUIRED>
+
+7.3. UDPoXML DTD
+
+ <!--
+ DTD for UDP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ -->
+
+ <!ELEMENT udp (udp.pseudoheader?, udp.header, payload)>
+
+ <!ELEMENT udp.header (src, dest, udp.length, checksum)>
+
+ <!ELEMENT udp.pseudoheader (source, destination, protocol,
+ udp.length)>
+
+ <!--
+ udp header + data length in octets. does not include the size of
+ the pseudoheader.
+ -->
+ <!ELEMENT udp.length EMPTY>
+ <!ATTLIST udp.length
+ value %Digits; #REQUIRED>
+
+
+
+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 <QtTest/QtTest>
+#include "private/qhttpnetworkconnection_p.h"
+#include "private/qnoncontiguousbytedevice_p.h"
+#include <QAuthenticator>
+
+#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<QSslError> &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<QString>("protocol");
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<ushort>("port");
+ QTest::addColumn<bool>("encrypt");
+ QTest::addColumn<int>("statusCode");
+ QTest::addColumn<QString>("statusString");
+ QTest::addColumn<int>("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<QString>("protocol");
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<ushort>("port");
+ QTest::addColumn<bool>("encrypt");
+ QTest::addColumn<int>("statusCode");
+ QTest::addColumn<QString>("statusString");
+ QTest::addColumn<int>("contentLength");
+ QTest::addColumn<int>("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<QString>("protocol");
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<ushort>("port");
+ QTest::addColumn<bool>("encrypt");
+ QTest::addColumn<QString>("data");
+ QTest::addColumn<bool>("succeed");
+
+ QTest::newRow("success-internal") << "http://" << QtNetworkSettings::serverName() << "/dav/file1.txt" << ushort(80) << false << "Hello World\nEnd of file\n"<<true;
+ QTest::newRow("fail-internal") << "http://" << QtNetworkSettings::serverName() << "/dav2/file1.txt" << ushort(80) << false << "Hello World\nEnd of file\n"<<false;
+ QTest::newRow("fail-host") << "http://" << "fluke-nosuchhost.troll.no" << "/dav2/file1.txt" << ushort(80) << false << "Hello World\nEnd of file\n"<<false;
+}
+
+void tst_QHttpNetworkConnection::put()
+{
+ QFETCH(QString, protocol);
+ QFETCH(QString, host);
+ QFETCH(QString, path);
+ QFETCH(ushort, port);
+ QFETCH(bool, encrypt);
+ QFETCH(QString, data);
+ QFETCH(bool, succeed);
+
+ QHttpNetworkConnection connection(host, port, encrypt);
+ QCOMPARE(connection.port(), port);
+ QCOMPARE(connection.hostName(), host);
+ QCOMPARE(connection.isSsl(), encrypt);
+
+ QHttpNetworkRequest request(protocol + host + path, QHttpNetworkRequest::Put);
+
+ QByteArray array = data.toLatin1();
+ QNonContiguousByteDevice *bd = QNonContiguousByteDeviceFactory::create(&array);
+ bd->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:" <<status;
+ QFAIL("Error in PUT");
+ }
+ } else {
+ if (!succeed) {
+ qDebug()<<"PUT Should fail, Status Code:" <<status;
+ QFAIL("Error in PUT");
+ }
+ }
+ delete reply;
+}
+
+void tst_QHttpNetworkConnection::post_data()
+{
+ QTest::addColumn<QString>("protocol");
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<ushort>("port");
+ QTest::addColumn<bool>("encrypt");
+ QTest::addColumn<QString>("data");
+ QTest::addColumn<int>("statusCode");
+ QTest::addColumn<QString>("statusString");
+ QTest::addColumn<int>("contentLength");
+ QTest::addColumn<int>("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<QHttpNetworkReply*>(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<QString>("protocol");
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<ushort>("port");
+ QTest::addColumn<bool>("encrypt");
+ QTest::addColumn<bool>("setCredentials");
+ QTest::addColumn<QString>("username");
+ QTest::addColumn<QString>("password");
+ QTest::addColumn<int>("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<QString>("protocol");
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<ushort>("port");
+ QTest::addColumn<bool>("encrypt");
+ QTest::addColumn<int>("statusCode");
+ QTest::addColumn<QString>("statusString");
+ QTest::addColumn<int>("contentLength");
+ QTest::addColumn<int>("downloadSize");
+ QTest::addColumn<bool>("autoCompress");
+ QTest::addColumn<QString>("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<QSslError> &errors)
+{
+ Q_UNUSED(errors)
+
+ QHttpNetworkReply *reply = qobject_cast<QHttpNetworkReply*>(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<QString>("protocol");
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<ushort>("port");
+ QTest::addColumn<bool>("encrypt");
+ QTest::addColumn<bool>("ignoreInit");
+ QTest::addColumn<bool>("ignoreFromSignal");
+ QTest::addColumn<int>("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<QSslError>&)),
+ SLOT(sslErrors(const QList<QSslError>&)));
+
+ 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<QString>("protocol");
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<ushort>("port");
+ QTest::addColumn<bool>("encrypt");
+ QTest::addColumn<QNetworkReply::NetworkError>("networkError");
+
+ QTest::newRow("protocol-error") << "https://" << QtNetworkSettings::serverName() << "/" << ushort(443) << true <<QNetworkReply::ProtocolUnknownError;
+}
+
+void tst_QHttpNetworkConnection::nossl()
+{
+ QFETCH(QString, protocol);
+ QFETCH(QString, host);
+ QFETCH(QString, path);
+ QFETCH(ushort, port);
+ QFETCH(bool, encrypt);
+ QFETCH(QNetworkReply::NetworkError, networkError);
+
+ QHttpNetworkConnection connection(host, port, encrypt);
+ QCOMPARE(connection.port(), port);
+ QCOMPARE(connection.hostName(), host);
+
+ QHttpNetworkRequest request(protocol + host + path);
+ QHttpNetworkReply *reply = connection.sendRequest(request);
+
+ finishedWithErrorCalled = false;
+ netErrorCode = QNetworkReply::NoError;
+
+ 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 (finishedWithErrorCalled)
+ break;
+ if (stopWatch.elapsed() >= 30000)
+ break;
+ }
+ QCOMPARE(netErrorCode, networkError);
+ delete reply;
+}
+#endif
+
+
+void tst_QHttpNetworkConnection::getMultiple_data()
+{
+ QTest::addColumn<quint16>("connectionCount");
+ QTest::addColumn<bool>("pipeliningAllowed");
+ // send 100 requests. apache will usually force-close after 100 requests in a single tcp connection
+ QTest::addColumn<int>("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<QHttpNetworkRequest*> requests;
+ QList<QHttpNetworkReply*> 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<QHttpNetworkRequest*> requests;
+ QList<QHttpNetworkReply*> 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<QHttpNetworkRequest*> requests;
+ QList<QHttpNetworkReply*> 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<QHttpNetworkRequest*> requests;
+ QList<QHttpNetworkReply*> 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<QHttpNetworkRequest*> requests;
+ QList<QHttpNetworkReply*> 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<bool>("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 <QtTest/QtTest>
+#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<QByteArray>("headers");
+ QTest::addColumn<QStringList>("fields");
+ QTest::addColumn<QStringList>("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 <QtTest/QtTest>
+
+#include <QtNetwork/QNetworkAccessManager>
+#include <QtNetwork/QNetworkConfigurationManager>
+
+#include <QtCore/QDebug>
+
+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>("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::NetworkAccessibility>(),
+ QNetworkAccessManager::NotAccessible);
+ QCOMPARE(manager.networkAccessible(), QNetworkAccessManager::NotAccessible);
+
+ manager.setNetworkAccessible(QNetworkAccessManager::Accessible);
+
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.takeFirst().at(0).value<QNetworkAccessManager::NetworkAccessibility>(),
+ 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::NetworkAccessibility>(),
+ 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 <QtTest/QtTest>
+#include <qabstractnetworkcache.h>
+
+#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<QDateTime>("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<QNetworkCacheMetaData>("data");
+ QTest::addColumn<bool>("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<QDateTime>("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<QNetworkCacheMetaData>("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<QNetworkCacheMetaData>("a");
+ QTest::addColumn<QNetworkCacheMetaData>("b");
+ QTest::addColumn<bool>("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<QNetworkCacheMetaData::RawHeaderList>("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<bool>("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<QUrl>("url");
+ QTest::addColumn<QUrl>("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 <QtTest/QtTest>
+#include <QtCore/QUrl>
+#include <QtNetwork/QNetworkCookie>
+
+
+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<QNetworkCookie> &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<QString>("cookieString");
+ QTest::addColumn<QNetworkCookie>("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<QNetworkCookie> 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<QString>("cookieString");
+ QTest::addColumn<QList<QNetworkCookie> >("expectedCookies");
+
+ QList<QNetworkCookie> 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<QNetworkCookie>() << 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<QNetworkCookie>() << 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<QNetworkCookie>();
+ 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<QNetworkCookie>, expectedCookies);
+
+ QList<QNetworkCookie> 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 <QtTest/QtTest>
+#include <QtNetwork/QNetworkCookieJar>
+#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<QNetworkCookie> &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<QNetworkCookie> allCookies() const
+ { return QNetworkCookieJar::allCookies(); }
+ inline void setAllCookies(const QList<QNetworkCookie> &cookieList)
+ { QNetworkCookieJar::setAllCookies(cookieList); }
+};
+
+void tst_QNetworkCookieJar::getterSetter()
+{
+ MyCookieJar jar;
+
+ QVERIFY(jar.allCookies().isEmpty());
+
+ QList<QNetworkCookie> list;
+ QNetworkCookie cookie;
+ cookie.setName("a");
+ list << cookie;
+
+ jar.setAllCookies(list);
+ QCOMPARE(jar.allCookies(), list);
+}
+
+void tst_QNetworkCookieJar::setCookiesFromUrl_data()
+{
+ QTest::addColumn<QList<QNetworkCookie> >("preset");
+ QTest::addColumn<QNetworkCookie>("newCookie");
+ QTest::addColumn<QString>("referenceUrl");
+ QTest::addColumn<QList<QNetworkCookie> >("expectedResult");
+ QTest::addColumn<bool>("setCookies");
+
+ QList<QNetworkCookie> preset;
+ QList<QNetworkCookie> 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<QNetworkCookie>, preset);
+ QFETCH(QNetworkCookie, newCookie);
+ QFETCH(QString, referenceUrl);
+ QFETCH(QList<QNetworkCookie>, expectedResult);
+ QFETCH(bool, setCookies);
+
+ QList<QNetworkCookie> cookieList;
+ cookieList += newCookie;
+ MyCookieJar jar;
+ jar.setAllCookies(preset);
+ QCOMPARE(jar.setCookiesFromUrl(cookieList, referenceUrl), setCookies);
+
+ QList<QNetworkCookie> 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<QList<QNetworkCookie> >("allCookies");
+ QTest::addColumn<QString>("url");
+ QTest::addColumn<QList<QNetworkCookie> >("expectedResult");
+
+ QList<QNetworkCookie> allCookies;
+ QList<QNetworkCookie> 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<QNetworkCookie>, allCookies);
+ QFETCH(QString, url);
+ QFETCH(QList<QNetworkCookie>, expectedResult);
+
+ MyCookieJar jar;
+ jar.setAllCookies(allCookies);
+
+ QList<QNetworkCookie> result = jar.cookiesForUrl(url);
+ QCOMPARE(result, expectedResult);
+}
+
+void tst_QNetworkCookieJar::effectiveTLDs_data()
+{
+ QTest::addColumn<QString>("domain");
+ QTest::addColumn<bool>("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 <QtTest/QtTest>
+#include <QtNetwork/QtNetwork>
+#include <qnetworkdiskcache.h>
+#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<QNetworkCacheMetaData>("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<QString>("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<int>("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:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ <ip>
+ <header length="474">
+ <version value="4"/>
+ <tos precedence="Routine" delay="Normal" throughput="Normal"
+ relibility="Normal" reserved="0"/>
+ <total.length value="461"/>
+ <id value="1"/>
+ <flags reserved="0" df="dont" mf="last"/>
+ <offset value="0"/>
+ <ttl value="255"/>
+ <protocol value="6"/>
+ <checksum value="8707"/>
+ <source address="10.0.0.22"/>
+ <destination address="10.0.0.1"/>
+ <options>
+ <end copied="0" class="0" number="0"/>
+ </options>
+ <padding pad="0"/>
+ </header>
+ <payload>
+ </payload>
+ </ip>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+3.2. Example Datagram
+
+ The following is an example TCPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ <tcp>
+ <tcp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <sequence number="322622954"/>
+ <acknowledgement number="689715995"/>
+ <offset number=""/>
+ <reserved value="0"/>
+ <control syn="1" ack="1"/>
+ <window size="1"/>
+ <urgent pointer="0"/>
+ <checksum value="2988"/>
+ <tcp.options>
+ <tcp.end kind="0"/>
+ </tcp.options>
+ <padding pad="0"/>
+ </tcp.header>
+ <payload>
+ </payload>
+ </tcp>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+4.2. Example Datagram
+
+ The following is an example UDPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ <udp>
+ <udp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <udp.length value="143"/>
+ <checksum value="2988"/>
+ </udp.header>
+ <payload>
+ </payload>
+ </udp>
+
+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 ("<?xml".)
+
+5.2. IEEE 802
+
+ BLOAT is encapsulated in IEEE 802 Networks as in [RFC1042] except
+ that the protocol type code for IPoXML is 0xBEEF.
+
+
+
+
+
+Kennedy Informational [Page 6]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+6. Gatewaying over IP
+
+ In order to facilitate the gradual introduction of BLOAT into the
+ public Internet, BLOAT MAY be encapsulated in IP as in [RFC2003] to
+ gateway between networks that run BLOAT natively on their LANs.
+
+7. DTDs
+
+ The Transport DTDs (7.2. and 7.3.) build on the definitions in the
+ Network DTD (7.1.)
+
+ The DTDs are referenced by their PubidLiteral and SystemLiteral (from
+ [XML]) although it is understood that most IPoXML implementations
+ will not need to pull down the DTD, as it will normally be embedded
+ in the implementation, and presents something of a catch-22 if you
+ need to load part of your network protocol over the network.
+
+7.1. IPoXML DTD
+
+ <!--
+ DTD for IP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ -->
+ <!--
+ DTD data types:
+
+ Digits [0..9]+
+
+ Precedence "NetworkControl | InternetworkControl |
+ CRITIC | FlashOverride | Flash | Immediate |
+ Priority | Routine"
+
+ IP4Addr "dotted-decimal" notation of [RFC1123]
+
+ Class [0..3]
+
+ Sec "Unclassified | Confidential | EFTO | MMMM | PROG |
+ Restricted | Secret | Top Secret | Reserved"
+
+ Compartments [0..65535]
+
+ Handling [0..65535]
+
+ TCC [0..16777216]
+
+ -->
+
+
+
+Kennedy Informational [Page 7]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ENTITY % Digits "CDATA">
+ <!ENTITY % Precedence "CDATA">
+ <!ENTITY % IP4Addr "CDATA">
+ <!ENTITY % Class "CDATA">
+ <!ENTITY % Sec "CDATA">
+ <!ENTITY % Compartments "CDATA">
+ <!ENTITY % Handling "CDATA">
+ <!ENTITY % TCC "CDATA">
+
+ <!ELEMENT ip (header, payload)>
+
+ <!ELEMENT header (version, tos, total.length, id, flags, offset, ttl,
+ protocol, checksum, source, destination, options,
+ padding)>
+ <!-- length of header in 32-bit words -->
+ <!ATTLIST header
+ length %Digits; #REQUIRED>
+
+ <!ELEMENT version EMPTY>
+ <!-- ip version. SHOULD be "4" -->
+ <!ATTLIST version
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT tos EMPTY>
+ <!ATTLIST tos
+ precedence %Precedence; #REQUIRED
+ delay (normal | low) #REQUIRED
+ throughput (normal | high) #REQUIRED
+ relibility (normal | high) #REQUIRED
+ reserved CDATA #FIXED "0">
+
+ <!ELEMENT total.length EMPTY>
+ <!--
+ total length of datagram (header and payload) in octets, MUST be
+ less than 65,535 (and SHOULD be less than 1024 for IPoXML on local
+ ethernets).
+ -->
+ <!ATTLIST total.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT id EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST id
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT flags EMPTY>
+ <!-- df = don't fragment, mf = more fragments -->
+ <!ATTLIST flags
+
+
+
+Kennedy Informational [Page 8]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ reserved CDATA #FIXED "0"
+ df (may|dont) #REQUIRED
+ mf (last|more) #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= offset <= 8192 measured in 8 octet (64-bit) chunks -->
+ <!ATTLIST offset
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT ttl EMPTY>
+ <!-- 0 <= ttl <= 255 -->
+ <!ATTLIST ttl
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT protocol EMPTY>
+ <!-- 0 <= protocol <= 255 (per IANA) -->
+ <!ATTLIST protocol
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT checksum EMPTY>
+ <!-- 0 <= checksum <= 65535 (over header only) -->
+ <!ATTLIST checksum
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT source EMPTY>
+ <!ATTLIST source
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT destination EMPTY>
+ <!ATTLIST destination
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT options ( end | noop | security | loose | strict | record
+ | stream | timestamp )*>
+
+ <!ELEMENT end EMPTY>
+ <!ATTLIST end
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "0">
+
+ <!ELEMENT noop EMPTY>
+ <!ATTLIST noop
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "1">
+
+ <!ELEMENT security EMPTY>
+
+
+
+Kennedy Informational [Page 9]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST security
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "2"
+ length CDATA #FIXED "11"
+ security %Sec; #REQUIRED
+ compartments %Compartments; #REQUIRED
+ handling %Handling; #REQUIRED
+ tcc %TCC; #REQUIRED>
+ <!ELEMENT loose (hop)+>
+ <!ATTLIST loose
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "3"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT hop EMPTY>
+ <!ATTLIST hop
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT strict (hop)+>
+ <!ATTLIST strict
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "9"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT record (hop)+>
+ <!ATTLIST record
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "7"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT stream EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST stream
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "8"
+ length CDATA #FIXED "4"
+ id %Digits; #REQUIRED>
+
+ <!ELEMENT timestamp (tstamp)+>
+ <!-- 0 <= oflw <=15 -->
+
+
+
+Kennedy Informational [Page 10]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST timestamp
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "2"
+ number CDATA #FIXED "4"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED
+ oflw %Digits; #REQUIRED
+ flag (0 | 1 | 3) #REQUIRED>
+
+ <!ELEMENT tstamp EMPTY>
+ <!ATTLIST tstamp
+ time %Digits; #REQUIRED
+ address %IP4Addr; #IMPLIED>
+ <!--
+ padding to bring header to 32-bit boundary.
+ pad MUST be "0"*
+ -->
+ <!ELEMENT padding EMPTY>
+ <!ATTLIST padding
+ pad CDATA #REQUIRED>
+
+ <!-- payload MUST be encoded as base-64 [RFC2045], as modified
+ by section 2.1 of this RFC -->
+ <!ELEMENT payload (CDATA)>
+
+7.2. TCPoXML DTD
+
+ <!--
+ DTD for TCP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ -->
+
+ <!-- the pseudoheader is only included for checksum calculations -->
+ <!ELEMENT tcp (tcp.pseudoheader?, tcp.header, payload)>
+
+ <!ELEMENT tcp.header (src, dest, sequence, acknowledgement, offset,
+ reserved, control, window, checksum, urgent,
+ tcp.options, padding)>
+
+ <!ELEMENT src EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+ <!ATTLIST src
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT dest EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+
+
+
+Kennedy Informational [Page 11]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST dest
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT sequence EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST sequence
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT acknowledgement EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST acknowledgement
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= number <= 255 -->
+ <!ATTLIST offset
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT reserved EMPTY>
+ <!ATTLIST reserved
+ value CDATA #FIXED "0">
+
+ <!ELEMENT control EMPTY>
+ <!ATTLIST control
+ urg (0|1) #IMPLIED
+ ack (0|1) #IMPLIED
+ psh (0|1) #IMPLIED
+ rst (0|1) #IMPLIED
+ syn (0|1) #IMPLIED
+ fin (0|1) #IMPLIED>
+
+ <!ELEMENT window EMPTY>
+ <!-- 0 <= size <= 65,535 -->
+ <!ATTLIST window
+ size %Digits; #REQUIRED>
+
+ <!--
+ checksum as in ip, but with
+ the following pseudo-header added into the tcp element:
+ -->
+ <!ELEMENT tcp.pseudoheader (source, destination, protocol,
+ tcp.length)>
+
+ <!--
+ tcp header + data length in octets. does not include the size of
+
+ the pseudoheader.
+ -->
+
+
+
+Kennedy Informational [Page 12]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ELEMENT tcp.length EMPTY>
+ <!ATTLIST tcp.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT urgent EMPTY>
+ <!-- 0 <= pointer <= 65,535 -->
+ <!ATTLIST urgent
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT tcp.options (tcp.end | tcp.noop | tcp.mss)+>
+
+ <!ELEMENT tcp.end EMPTY>
+ <!ATTLIST tcp.end
+ kind CDATA #FIXED "0">
+
+ <!ELEMENT tcp.noop EMPTY>
+ <!ATTLIST tcp.noop
+ kind CDATA #FIXED "1">
+
+ <!ELEMENT tcp.mss EMPTY>
+ <!ATTLIST tcp.mss
+ kind CDATA #FIXED "2"
+ length CDATA #FIXED "4"
+ size %Digits; #REQUIRED>
+
+7.3. UDPoXML DTD
+
+ <!--
+ DTD for UDP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ -->
+
+ <!ELEMENT udp (udp.pseudoheader?, udp.header, payload)>
+
+ <!ELEMENT udp.header (src, dest, udp.length, checksum)>
+
+ <!ELEMENT udp.pseudoheader (source, destination, protocol,
+ udp.length)>
+
+ <!--
+ udp header + data length in octets. does not include the size of
+ the pseudoheader.
+ -->
+ <!ELEMENT udp.length EMPTY>
+ <!ATTLIST udp.length
+ value %Digits; #REQUIRED>
+
+
+
+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:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ <ip>
+ <header length="474">
+ <version value="4"/>
+ <tos precedence="Routine" delay="Normal" throughput="Normal"
+ relibility="Normal" reserved="0"/>
+ <total.length value="461"/>
+ <id value="1"/>
+ <flags reserved="0" df="dont" mf="last"/>
+ <offset value="0"/>
+ <ttl value="255"/>
+ <protocol value="6"/>
+ <checksum value="8707"/>
+ <source address="10.0.0.22"/>
+ <destination address="10.0.0.1"/>
+ <options>
+ <end copied="0" class="0" number="0"/>
+ </options>
+ <padding pad="0"/>
+ </header>
+ <payload>
+ </payload>
+ </ip>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+3.2. Example Datagram
+
+ The following is an example TCPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ <tcp>
+ <tcp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <sequence number="322622954"/>
+ <acknowledgement number="689715995"/>
+ <offset number=""/>
+ <reserved value="0"/>
+ <control syn="1" ack="1"/>
+ <window size="1"/>
+ <urgent pointer="0"/>
+ <checksum value="2988"/>
+ <tcp.options>
+ <tcp.end kind="0"/>
+ </tcp.options>
+ <padding pad="0"/>
+ </tcp.header>
+ <payload>
+ </payload>
+ </tcp>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+4.2. Example Datagram
+
+ The following is an example UDPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ <udp>
+ <udp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <udp.length value="143"/>
+ <checksum value="2988"/>
+ </udp.header>
+ <payload>
+ </payload>
+ </udp>
+
+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 ("<?xml".)
+
+5.2. IEEE 802
+
+ BLOAT is encapsulated in IEEE 802 Networks as in [RFC1042] except
+ that the protocol type code for IPoXML is 0xBEEF.
+
+
+
+
+
+Kennedy Informational [Page 6]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+6. Gatewaying over IP
+
+ In order to facilitate the gradual introduction of BLOAT into the
+ public Internet, BLOAT MAY be encapsulated in IP as in [RFC2003] to
+ gateway between networks that run BLOAT natively on their LANs.
+
+7. DTDs
+
+ The Transport DTDs (7.2. and 7.3.) build on the definitions in the
+ Network DTD (7.1.)
+
+ The DTDs are referenced by their PubidLiteral and SystemLiteral (from
+ [XML]) although it is understood that most IPoXML implementations
+ will not need to pull down the DTD, as it will normally be embedded
+ in the implementation, and presents something of a catch-22 if you
+ need to load part of your network protocol over the network.
+
+7.1. IPoXML DTD
+
+ <!--
+ DTD for IP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ -->
+ <!--
+ DTD data types:
+
+ Digits [0..9]+
+
+ Precedence "NetworkControl | InternetworkControl |
+ CRITIC | FlashOverride | Flash | Immediate |
+ Priority | Routine"
+
+ IP4Addr "dotted-decimal" notation of [RFC1123]
+
+ Class [0..3]
+
+ Sec "Unclassified | Confidential | EFTO | MMMM | PROG |
+ Restricted | Secret | Top Secret | Reserved"
+
+ Compartments [0..65535]
+
+ Handling [0..65535]
+
+ TCC [0..16777216]
+
+ -->
+
+
+
+Kennedy Informational [Page 7]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ENTITY % Digits "CDATA">
+ <!ENTITY % Precedence "CDATA">
+ <!ENTITY % IP4Addr "CDATA">
+ <!ENTITY % Class "CDATA">
+ <!ENTITY % Sec "CDATA">
+ <!ENTITY % Compartments "CDATA">
+ <!ENTITY % Handling "CDATA">
+ <!ENTITY % TCC "CDATA">
+
+ <!ELEMENT ip (header, payload)>
+
+ <!ELEMENT header (version, tos, total.length, id, flags, offset, ttl,
+ protocol, checksum, source, destination, options,
+ padding)>
+ <!-- length of header in 32-bit words -->
+ <!ATTLIST header
+ length %Digits; #REQUIRED>
+
+ <!ELEMENT version EMPTY>
+ <!-- ip version. SHOULD be "4" -->
+ <!ATTLIST version
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT tos EMPTY>
+ <!ATTLIST tos
+ precedence %Precedence; #REQUIRED
+ delay (normal | low) #REQUIRED
+ throughput (normal | high) #REQUIRED
+ relibility (normal | high) #REQUIRED
+ reserved CDATA #FIXED "0">
+
+ <!ELEMENT total.length EMPTY>
+ <!--
+ total length of datagram (header and payload) in octets, MUST be
+ less than 65,535 (and SHOULD be less than 1024 for IPoXML on local
+ ethernets).
+ -->
+ <!ATTLIST total.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT id EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST id
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT flags EMPTY>
+ <!-- df = don't fragment, mf = more fragments -->
+ <!ATTLIST flags
+
+
+
+Kennedy Informational [Page 8]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ reserved CDATA #FIXED "0"
+ df (may|dont) #REQUIRED
+ mf (last|more) #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= offset <= 8192 measured in 8 octet (64-bit) chunks -->
+ <!ATTLIST offset
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT ttl EMPTY>
+ <!-- 0 <= ttl <= 255 -->
+ <!ATTLIST ttl
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT protocol EMPTY>
+ <!-- 0 <= protocol <= 255 (per IANA) -->
+ <!ATTLIST protocol
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT checksum EMPTY>
+ <!-- 0 <= checksum <= 65535 (over header only) -->
+ <!ATTLIST checksum
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT source EMPTY>
+ <!ATTLIST source
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT destination EMPTY>
+ <!ATTLIST destination
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT options ( end | noop | security | loose | strict | record
+ | stream | timestamp )*>
+
+ <!ELEMENT end EMPTY>
+ <!ATTLIST end
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "0">
+
+ <!ELEMENT noop EMPTY>
+ <!ATTLIST noop
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "1">
+
+ <!ELEMENT security EMPTY>
+
+
+
+Kennedy Informational [Page 9]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST security
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "2"
+ length CDATA #FIXED "11"
+ security %Sec; #REQUIRED
+ compartments %Compartments; #REQUIRED
+ handling %Handling; #REQUIRED
+ tcc %TCC; #REQUIRED>
+ <!ELEMENT loose (hop)+>
+ <!ATTLIST loose
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "3"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT hop EMPTY>
+ <!ATTLIST hop
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT strict (hop)+>
+ <!ATTLIST strict
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "9"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT record (hop)+>
+ <!ATTLIST record
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "7"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT stream EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST stream
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "8"
+ length CDATA #FIXED "4"
+ id %Digits; #REQUIRED>
+
+ <!ELEMENT timestamp (tstamp)+>
+ <!-- 0 <= oflw <=15 -->
+
+
+
+Kennedy Informational [Page 10]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST timestamp
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "2"
+ number CDATA #FIXED "4"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED
+ oflw %Digits; #REQUIRED
+ flag (0 | 1 | 3) #REQUIRED>
+
+ <!ELEMENT tstamp EMPTY>
+ <!ATTLIST tstamp
+ time %Digits; #REQUIRED
+ address %IP4Addr; #IMPLIED>
+ <!--
+ padding to bring header to 32-bit boundary.
+ pad MUST be "0"*
+ -->
+ <!ELEMENT padding EMPTY>
+ <!ATTLIST padding
+ pad CDATA #REQUIRED>
+
+ <!-- payload MUST be encoded as base-64 [RFC2045], as modified
+ by section 2.1 of this RFC -->
+ <!ELEMENT payload (CDATA)>
+
+7.2. TCPoXML DTD
+
+ <!--
+ DTD for TCP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ -->
+
+ <!-- the pseudoheader is only included for checksum calculations -->
+ <!ELEMENT tcp (tcp.pseudoheader?, tcp.header, payload)>
+
+ <!ELEMENT tcp.header (src, dest, sequence, acknowledgement, offset,
+ reserved, control, window, checksum, urgent,
+ tcp.options, padding)>
+
+ <!ELEMENT src EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+ <!ATTLIST src
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT dest EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+
+
+
+Kennedy Informational [Page 11]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST dest
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT sequence EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST sequence
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT acknowledgement EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST acknowledgement
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= number <= 255 -->
+ <!ATTLIST offset
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT reserved EMPTY>
+ <!ATTLIST reserved
+ value CDATA #FIXED "0">
+
+ <!ELEMENT control EMPTY>
+ <!ATTLIST control
+ urg (0|1) #IMPLIED
+ ack (0|1) #IMPLIED
+ psh (0|1) #IMPLIED
+ rst (0|1) #IMPLIED
+ syn (0|1) #IMPLIED
+ fin (0|1) #IMPLIED>
+
+ <!ELEMENT window EMPTY>
+ <!-- 0 <= size <= 65,535 -->
+ <!ATTLIST window
+ size %Digits; #REQUIRED>
+
+ <!--
+ checksum as in ip, but with
+ the following pseudo-header added into the tcp element:
+ -->
+ <!ELEMENT tcp.pseudoheader (source, destination, protocol,
+ tcp.length)>
+
+ <!--
+ tcp header + data length in octets. does not include the size of
+
+ the pseudoheader.
+ -->
+
+
+
+Kennedy Informational [Page 12]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ELEMENT tcp.length EMPTY>
+ <!ATTLIST tcp.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT urgent EMPTY>
+ <!-- 0 <= pointer <= 65,535 -->
+ <!ATTLIST urgent
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT tcp.options (tcp.end | tcp.noop | tcp.mss)+>
+
+ <!ELEMENT tcp.end EMPTY>
+ <!ATTLIST tcp.end
+ kind CDATA #FIXED "0">
+
+ <!ELEMENT tcp.noop EMPTY>
+ <!ATTLIST tcp.noop
+ kind CDATA #FIXED "1">
+
+ <!ELEMENT tcp.mss EMPTY>
+ <!ATTLIST tcp.mss
+ kind CDATA #FIXED "2"
+ length CDATA #FIXED "4"
+ size %Digits; #REQUIRED>
+
+7.3. UDPoXML DTD
+
+ <!--
+ DTD for UDP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ -->
+
+ <!ELEMENT udp (udp.pseudoheader?, udp.header, payload)>
+
+ <!ELEMENT udp.header (src, dest, udp.length, checksum)>
+
+ <!ELEMENT udp.pseudoheader (source, destination, protocol,
+ udp.length)>
+
+ <!--
+ udp header + data length in octets. does not include the size of
+ the pseudoheader.
+ -->
+ <!ELEMENT udp.length EMPTY>
+ <!ATTLIST udp.length
+ value %Digits; #REQUIRED>
+
+
+
+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:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ <ip>
+ <header length="474">
+ <version value="4"/>
+ <tos precedence="Routine" delay="Normal" throughput="Normal"
+ relibility="Normal" reserved="0"/>
+ <total.length value="461"/>
+ <id value="1"/>
+ <flags reserved="0" df="dont" mf="last"/>
+ <offset value="0"/>
+ <ttl value="255"/>
+ <protocol value="6"/>
+ <checksum value="8707"/>
+ <source address="10.0.0.22"/>
+ <destination address="10.0.0.1"/>
+ <options>
+ <end copied="0" class="0" number="0"/>
+ </options>
+ <padding pad="0"/>
+ </header>
+ <payload>
+ </payload>
+ </ip>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+3.2. Example Datagram
+
+ The following is an example TCPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ <tcp>
+ <tcp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <sequence number="322622954"/>
+ <acknowledgement number="689715995"/>
+ <offset number=""/>
+ <reserved value="0"/>
+ <control syn="1" ack="1"/>
+ <window size="1"/>
+ <urgent pointer="0"/>
+ <checksum value="2988"/>
+ <tcp.options>
+ <tcp.end kind="0"/>
+ </tcp.options>
+ <padding pad="0"/>
+ </tcp.header>
+ <payload>
+ </payload>
+ </tcp>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+4.2. Example Datagram
+
+ The following is an example UDPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ <udp>
+ <udp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <udp.length value="143"/>
+ <checksum value="2988"/>
+ </udp.header>
+ <payload>
+ </payload>
+ </udp>
+
+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 ("<?xml".)
+
+5.2. IEEE 802
+
+ BLOAT is encapsulated in IEEE 802 Networks as in [RFC1042] except
+ that the protocol type code for IPoXML is 0xBEEF.
+
+
+
+
+
+Kennedy Informational [Page 6]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+6. Gatewaying over IP
+
+ In order to facilitate the gradual introduction of BLOAT into the
+ public Internet, BLOAT MAY be encapsulated in IP as in [RFC2003] to
+ gateway between networks that run BLOAT natively on their LANs.
+
+7. DTDs
+
+ The Transport DTDs (7.2. and 7.3.) build on the definitions in the
+ Network DTD (7.1.)
+
+ The DTDs are referenced by their PubidLiteral and SystemLiteral (from
+ [XML]) although it is understood that most IPoXML implementations
+ will not need to pull down the DTD, as it will normally be embedded
+ in the implementation, and presents something of a catch-22 if you
+ need to load part of your network protocol over the network.
+
+7.1. IPoXML DTD
+
+ <!--
+ DTD for IP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ -->
+ <!--
+ DTD data types:
+
+ Digits [0..9]+
+
+ Precedence "NetworkControl | InternetworkControl |
+ CRITIC | FlashOverride | Flash | Immediate |
+ Priority | Routine"
+
+ IP4Addr "dotted-decimal" notation of [RFC1123]
+
+ Class [0..3]
+
+ Sec "Unclassified | Confidential | EFTO | MMMM | PROG |
+ Restricted | Secret | Top Secret | Reserved"
+
+ Compartments [0..65535]
+
+ Handling [0..65535]
+
+ TCC [0..16777216]
+
+ -->
+
+
+
+Kennedy Informational [Page 7]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ENTITY % Digits "CDATA">
+ <!ENTITY % Precedence "CDATA">
+ <!ENTITY % IP4Addr "CDATA">
+ <!ENTITY % Class "CDATA">
+ <!ENTITY % Sec "CDATA">
+ <!ENTITY % Compartments "CDATA">
+ <!ENTITY % Handling "CDATA">
+ <!ENTITY % TCC "CDATA">
+
+ <!ELEMENT ip (header, payload)>
+
+ <!ELEMENT header (version, tos, total.length, id, flags, offset, ttl,
+ protocol, checksum, source, destination, options,
+ padding)>
+ <!-- length of header in 32-bit words -->
+ <!ATTLIST header
+ length %Digits; #REQUIRED>
+
+ <!ELEMENT version EMPTY>
+ <!-- ip version. SHOULD be "4" -->
+ <!ATTLIST version
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT tos EMPTY>
+ <!ATTLIST tos
+ precedence %Precedence; #REQUIRED
+ delay (normal | low) #REQUIRED
+ throughput (normal | high) #REQUIRED
+ relibility (normal | high) #REQUIRED
+ reserved CDATA #FIXED "0">
+
+ <!ELEMENT total.length EMPTY>
+ <!--
+ total length of datagram (header and payload) in octets, MUST be
+ less than 65,535 (and SHOULD be less than 1024 for IPoXML on local
+ ethernets).
+ -->
+ <!ATTLIST total.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT id EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST id
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT flags EMPTY>
+ <!-- df = don't fragment, mf = more fragments -->
+ <!ATTLIST flags
+
+
+
+Kennedy Informational [Page 8]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ reserved CDATA #FIXED "0"
+ df (may|dont) #REQUIRED
+ mf (last|more) #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= offset <= 8192 measured in 8 octet (64-bit) chunks -->
+ <!ATTLIST offset
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT ttl EMPTY>
+ <!-- 0 <= ttl <= 255 -->
+ <!ATTLIST ttl
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT protocol EMPTY>
+ <!-- 0 <= protocol <= 255 (per IANA) -->
+ <!ATTLIST protocol
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT checksum EMPTY>
+ <!-- 0 <= checksum <= 65535 (over header only) -->
+ <!ATTLIST checksum
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT source EMPTY>
+ <!ATTLIST source
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT destination EMPTY>
+ <!ATTLIST destination
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT options ( end | noop | security | loose | strict | record
+ | stream | timestamp )*>
+
+ <!ELEMENT end EMPTY>
+ <!ATTLIST end
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "0">
+
+ <!ELEMENT noop EMPTY>
+ <!ATTLIST noop
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "1">
+
+ <!ELEMENT security EMPTY>
+
+
+
+Kennedy Informational [Page 9]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST security
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "2"
+ length CDATA #FIXED "11"
+ security %Sec; #REQUIRED
+ compartments %Compartments; #REQUIRED
+ handling %Handling; #REQUIRED
+ tcc %TCC; #REQUIRED>
+ <!ELEMENT loose (hop)+>
+ <!ATTLIST loose
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "3"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT hop EMPTY>
+ <!ATTLIST hop
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT strict (hop)+>
+ <!ATTLIST strict
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "9"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT record (hop)+>
+ <!ATTLIST record
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "7"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT stream EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST stream
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "8"
+ length CDATA #FIXED "4"
+ id %Digits; #REQUIRED>
+
+ <!ELEMENT timestamp (tstamp)+>
+ <!-- 0 <= oflw <=15 -->
+
+
+
+Kennedy Informational [Page 10]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST timestamp
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "2"
+ number CDATA #FIXED "4"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED
+ oflw %Digits; #REQUIRED
+ flag (0 | 1 | 3) #REQUIRED>
+
+ <!ELEMENT tstamp EMPTY>
+ <!ATTLIST tstamp
+ time %Digits; #REQUIRED
+ address %IP4Addr; #IMPLIED>
+ <!--
+ padding to bring header to 32-bit boundary.
+ pad MUST be "0"*
+ -->
+ <!ELEMENT padding EMPTY>
+ <!ATTLIST padding
+ pad CDATA #REQUIRED>
+
+ <!-- payload MUST be encoded as base-64 [RFC2045], as modified
+ by section 2.1 of this RFC -->
+ <!ELEMENT payload (CDATA)>
+
+7.2. TCPoXML DTD
+
+ <!--
+ DTD for TCP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ -->
+
+ <!-- the pseudoheader is only included for checksum calculations -->
+ <!ELEMENT tcp (tcp.pseudoheader?, tcp.header, payload)>
+
+ <!ELEMENT tcp.header (src, dest, sequence, acknowledgement, offset,
+ reserved, control, window, checksum, urgent,
+ tcp.options, padding)>
+
+ <!ELEMENT src EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+ <!ATTLIST src
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT dest EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+
+
+
+Kennedy Informational [Page 11]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST dest
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT sequence EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST sequence
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT acknowledgement EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST acknowledgement
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= number <= 255 -->
+ <!ATTLIST offset
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT reserved EMPTY>
+ <!ATTLIST reserved
+ value CDATA #FIXED "0">
+
+ <!ELEMENT control EMPTY>
+ <!ATTLIST control
+ urg (0|1) #IMPLIED
+ ack (0|1) #IMPLIED
+ psh (0|1) #IMPLIED
+ rst (0|1) #IMPLIED
+ syn (0|1) #IMPLIED
+ fin (0|1) #IMPLIED>
+
+ <!ELEMENT window EMPTY>
+ <!-- 0 <= size <= 65,535 -->
+ <!ATTLIST window
+ size %Digits; #REQUIRED>
+
+ <!--
+ checksum as in ip, but with
+ the following pseudo-header added into the tcp element:
+ -->
+ <!ELEMENT tcp.pseudoheader (source, destination, protocol,
+ tcp.length)>
+
+ <!--
+ tcp header + data length in octets. does not include the size of
+
+ the pseudoheader.
+ -->
+
+
+
+Kennedy Informational [Page 12]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ELEMENT tcp.length EMPTY>
+ <!ATTLIST tcp.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT urgent EMPTY>
+ <!-- 0 <= pointer <= 65,535 -->
+ <!ATTLIST urgent
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT tcp.options (tcp.end | tcp.noop | tcp.mss)+>
+
+ <!ELEMENT tcp.end EMPTY>
+ <!ATTLIST tcp.end
+ kind CDATA #FIXED "0">
+
+ <!ELEMENT tcp.noop EMPTY>
+ <!ATTLIST tcp.noop
+ kind CDATA #FIXED "1">
+
+ <!ELEMENT tcp.mss EMPTY>
+ <!ATTLIST tcp.mss
+ kind CDATA #FIXED "2"
+ length CDATA #FIXED "4"
+ size %Digits; #REQUIRED>
+
+7.3. UDPoXML DTD
+
+ <!--
+ DTD for UDP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ -->
+
+ <!ELEMENT udp (udp.pseudoheader?, udp.header, payload)>
+
+ <!ELEMENT udp.header (src, dest, udp.length, checksum)>
+
+ <!ELEMENT udp.pseudoheader (source, destination, protocol,
+ udp.length)>
+
+ <!--
+ udp header + data length in octets. does not include the size of
+ the pseudoheader.
+ -->
+ <!ELEMENT udp.length EMPTY>
+ <!ATTLIST udp.length
+ value %Digits; #REQUIRED>
+
+
+
+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:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ <ip>
+ <header length="474">
+ <version value="4"/>
+ <tos precedence="Routine" delay="Normal" throughput="Normal"
+ relibility="Normal" reserved="0"/>
+ <total.length value="461"/>
+ <id value="1"/>
+ <flags reserved="0" df="dont" mf="last"/>
+ <offset value="0"/>
+ <ttl value="255"/>
+ <protocol value="6"/>
+ <checksum value="8707"/>
+ <source address="10.0.0.22"/>
+ <destination address="10.0.0.1"/>
+ <options>
+ <end copied="0" class="0" number="0"/>
+ </options>
+ <padding pad="0"/>
+ </header>
+ <payload>
+ </payload>
+ </ip>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+3.2. Example Datagram
+
+ The following is an example TCPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ <tcp>
+ <tcp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <sequence number="322622954"/>
+ <acknowledgement number="689715995"/>
+ <offset number=""/>
+ <reserved value="0"/>
+ <control syn="1" ack="1"/>
+ <window size="1"/>
+ <urgent pointer="0"/>
+ <checksum value="2988"/>
+ <tcp.options>
+ <tcp.end kind="0"/>
+ </tcp.options>
+ <padding pad="0"/>
+ </tcp.header>
+ <payload>
+ </payload>
+ </tcp>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+4.2. Example Datagram
+
+ The following is an example UDPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ <udp>
+ <udp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <udp.length value="143"/>
+ <checksum value="2988"/>
+ </udp.header>
+ <payload>
+ </payload>
+ </udp>
+
+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 ("<?xml".)
+
+5.2. IEEE 802
+
+ BLOAT is encapsulated in IEEE 802 Networks as in [RFC1042] except
+ that the protocol type code for IPoXML is 0xBEEF.
+
+
+
+
+
+Kennedy Informational [Page 6]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+6. Gatewaying over IP
+
+ In order to facilitate the gradual introduction of BLOAT into the
+ public Internet, BLOAT MAY be encapsulated in IP as in [RFC2003] to
+ gateway between networks that run BLOAT natively on their LANs.
+
+7. DTDs
+
+ The Transport DTDs (7.2. and 7.3.) build on the definitions in the
+ Network DTD (7.1.)
+
+ The DTDs are referenced by their PubidLiteral and SystemLiteral (from
+ [XML]) although it is understood that most IPoXML implementations
+ will not need to pull down the DTD, as it will normally be embedded
+ in the implementation, and presents something of a catch-22 if you
+ need to load part of your network protocol over the network.
+
+7.1. IPoXML DTD
+
+ <!--
+ DTD for IP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ -->
+ <!--
+ DTD data types:
+
+ Digits [0..9]+
+
+ Precedence "NetworkControl | InternetworkControl |
+ CRITIC | FlashOverride | Flash | Immediate |
+ Priority | Routine"
+
+ IP4Addr "dotted-decimal" notation of [RFC1123]
+
+ Class [0..3]
+
+ Sec "Unclassified | Confidential | EFTO | MMMM | PROG |
+ Restricted | Secret | Top Secret | Reserved"
+
+ Compartments [0..65535]
+
+ Handling [0..65535]
+
+ TCC [0..16777216]
+
+ -->
+
+
+
+Kennedy Informational [Page 7]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ENTITY % Digits "CDATA">
+ <!ENTITY % Precedence "CDATA">
+ <!ENTITY % IP4Addr "CDATA">
+ <!ENTITY % Class "CDATA">
+ <!ENTITY % Sec "CDATA">
+ <!ENTITY % Compartments "CDATA">
+ <!ENTITY % Handling "CDATA">
+ <!ENTITY % TCC "CDATA">
+
+ <!ELEMENT ip (header, payload)>
+
+ <!ELEMENT header (version, tos, total.length, id, flags, offset, ttl,
+ protocol, checksum, source, destination, options,
+ padding)>
+ <!-- length of header in 32-bit words -->
+ <!ATTLIST header
+ length %Digits; #REQUIRED>
+
+ <!ELEMENT version EMPTY>
+ <!-- ip version. SHOULD be "4" -->
+ <!ATTLIST version
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT tos EMPTY>
+ <!ATTLIST tos
+ precedence %Precedence; #REQUIRED
+ delay (normal | low) #REQUIRED
+ throughput (normal | high) #REQUIRED
+ relibility (normal | high) #REQUIRED
+ reserved CDATA #FIXED "0">
+
+ <!ELEMENT total.length EMPTY>
+ <!--
+ total length of datagram (header and payload) in octets, MUST be
+ less than 65,535 (and SHOULD be less than 1024 for IPoXML on local
+ ethernets).
+ -->
+ <!ATTLIST total.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT id EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST id
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT flags EMPTY>
+ <!-- df = don't fragment, mf = more fragments -->
+ <!ATTLIST flags
+
+
+
+Kennedy Informational [Page 8]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ reserved CDATA #FIXED "0"
+ df (may|dont) #REQUIRED
+ mf (last|more) #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= offset <= 8192 measured in 8 octet (64-bit) chunks -->
+ <!ATTLIST offset
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT ttl EMPTY>
+ <!-- 0 <= ttl <= 255 -->
+ <!ATTLIST ttl
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT protocol EMPTY>
+ <!-- 0 <= protocol <= 255 (per IANA) -->
+ <!ATTLIST protocol
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT checksum EMPTY>
+ <!-- 0 <= checksum <= 65535 (over header only) -->
+ <!ATTLIST checksum
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT source EMPTY>
+ <!ATTLIST source
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT destination EMPTY>
+ <!ATTLIST destination
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT options ( end | noop | security | loose | strict | record
+ | stream | timestamp )*>
+
+ <!ELEMENT end EMPTY>
+ <!ATTLIST end
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "0">
+
+ <!ELEMENT noop EMPTY>
+ <!ATTLIST noop
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "1">
+
+ <!ELEMENT security EMPTY>
+
+
+
+Kennedy Informational [Page 9]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST security
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "2"
+ length CDATA #FIXED "11"
+ security %Sec; #REQUIRED
+ compartments %Compartments; #REQUIRED
+ handling %Handling; #REQUIRED
+ tcc %TCC; #REQUIRED>
+ <!ELEMENT loose (hop)+>
+ <!ATTLIST loose
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "3"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT hop EMPTY>
+ <!ATTLIST hop
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT strict (hop)+>
+ <!ATTLIST strict
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "9"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT record (hop)+>
+ <!ATTLIST record
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "7"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT stream EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST stream
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "8"
+ length CDATA #FIXED "4"
+ id %Digits; #REQUIRED>
+
+ <!ELEMENT timestamp (tstamp)+>
+ <!-- 0 <= oflw <=15 -->
+
+
+
+Kennedy Informational [Page 10]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST timestamp
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "2"
+ number CDATA #FIXED "4"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED
+ oflw %Digits; #REQUIRED
+ flag (0 | 1 | 3) #REQUIRED>
+
+ <!ELEMENT tstamp EMPTY>
+ <!ATTLIST tstamp
+ time %Digits; #REQUIRED
+ address %IP4Addr; #IMPLIED>
+ <!--
+ padding to bring header to 32-bit boundary.
+ pad MUST be "0"*
+ -->
+ <!ELEMENT padding EMPTY>
+ <!ATTLIST padding
+ pad CDATA #REQUIRED>
+
+ <!-- payload MUST be encoded as base-64 [RFC2045], as modified
+ by section 2.1 of this RFC -->
+ <!ELEMENT payload (CDATA)>
+
+7.2. TCPoXML DTD
+
+ <!--
+ DTD for TCP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ -->
+
+ <!-- the pseudoheader is only included for checksum calculations -->
+ <!ELEMENT tcp (tcp.pseudoheader?, tcp.header, payload)>
+
+ <!ELEMENT tcp.header (src, dest, sequence, acknowledgement, offset,
+ reserved, control, window, checksum, urgent,
+ tcp.options, padding)>
+
+ <!ELEMENT src EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+ <!ATTLIST src
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT dest EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+
+
+
+Kennedy Informational [Page 11]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST dest
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT sequence EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST sequence
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT acknowledgement EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST acknowledgement
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= number <= 255 -->
+ <!ATTLIST offset
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT reserved EMPTY>
+ <!ATTLIST reserved
+ value CDATA #FIXED "0">
+
+ <!ELEMENT control EMPTY>
+ <!ATTLIST control
+ urg (0|1) #IMPLIED
+ ack (0|1) #IMPLIED
+ psh (0|1) #IMPLIED
+ rst (0|1) #IMPLIED
+ syn (0|1) #IMPLIED
+ fin (0|1) #IMPLIED>
+
+ <!ELEMENT window EMPTY>
+ <!-- 0 <= size <= 65,535 -->
+ <!ATTLIST window
+ size %Digits; #REQUIRED>
+
+ <!--
+ checksum as in ip, but with
+ the following pseudo-header added into the tcp element:
+ -->
+ <!ELEMENT tcp.pseudoheader (source, destination, protocol,
+ tcp.length)>
+
+ <!--
+ tcp header + data length in octets. does not include the size of
+
+ the pseudoheader.
+ -->
+
+
+
+Kennedy Informational [Page 12]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ELEMENT tcp.length EMPTY>
+ <!ATTLIST tcp.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT urgent EMPTY>
+ <!-- 0 <= pointer <= 65,535 -->
+ <!ATTLIST urgent
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT tcp.options (tcp.end | tcp.noop | tcp.mss)+>
+
+ <!ELEMENT tcp.end EMPTY>
+ <!ATTLIST tcp.end
+ kind CDATA #FIXED "0">
+
+ <!ELEMENT tcp.noop EMPTY>
+ <!ATTLIST tcp.noop
+ kind CDATA #FIXED "1">
+
+ <!ELEMENT tcp.mss EMPTY>
+ <!ATTLIST tcp.mss
+ kind CDATA #FIXED "2"
+ length CDATA #FIXED "4"
+ size %Digits; #REQUIRED>
+
+7.3. UDPoXML DTD
+
+ <!--
+ DTD for UDP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ -->
+
+ <!ELEMENT udp (udp.pseudoheader?, udp.header, payload)>
+
+ <!ELEMENT udp.header (src, dest, udp.length, checksum)>
+
+ <!ELEMENT udp.pseudoheader (source, destination, protocol,
+ udp.length)>
+
+ <!--
+ udp header + data length in octets. does not include the size of
+ the pseudoheader.
+ -->
+ <!ELEMENT udp.length EMPTY>
+ <!ATTLIST udp.length
+ value %Digits; #REQUIRED>
+
+
+
+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:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ <ip>
+ <header length="474">
+ <version value="4"/>
+ <tos precedence="Routine" delay="Normal" throughput="Normal"
+ relibility="Normal" reserved="0"/>
+ <total.length value="461"/>
+ <id value="1"/>
+ <flags reserved="0" df="dont" mf="last"/>
+ <offset value="0"/>
+ <ttl value="255"/>
+ <protocol value="6"/>
+ <checksum value="8707"/>
+ <source address="10.0.0.22"/>
+ <destination address="10.0.0.1"/>
+ <options>
+ <end copied="0" class="0" number="0"/>
+ </options>
+ <padding pad="0"/>
+ </header>
+ <payload>
+ </payload>
+ </ip>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+3.2. Example Datagram
+
+ The following is an example TCPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ <tcp>
+ <tcp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <sequence number="322622954"/>
+ <acknowledgement number="689715995"/>
+ <offset number=""/>
+ <reserved value="0"/>
+ <control syn="1" ack="1"/>
+ <window size="1"/>
+ <urgent pointer="0"/>
+ <checksum value="2988"/>
+ <tcp.options>
+ <tcp.end kind="0"/>
+ </tcp.options>
+ <padding pad="0"/>
+ </tcp.header>
+ <payload>
+ </payload>
+ </tcp>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+4.2. Example Datagram
+
+ The following is an example UDPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ <udp>
+ <udp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <udp.length value="143"/>
+ <checksum value="2988"/>
+ </udp.header>
+ <payload>
+ </payload>
+ </udp>
+
+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 ("<?xml".)
+
+5.2. IEEE 802
+
+ BLOAT is encapsulated in IEEE 802 Networks as in [RFC1042] except
+ that the protocol type code for IPoXML is 0xBEEF.
+
+
+
+
+
+Kennedy Informational [Page 6]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+6. Gatewaying over IP
+
+ In order to facilitate the gradual introduction of BLOAT into the
+ public Internet, BLOAT MAY be encapsulated in IP as in [RFC2003] to
+ gateway between networks that run BLOAT natively on their LANs.
+
+7. DTDs
+
+ The Transport DTDs (7.2. and 7.3.) build on the definitions in the
+ Network DTD (7.1.)
+
+ The DTDs are referenced by their PubidLiteral and SystemLiteral (from
+ [XML]) although it is understood that most IPoXML implementations
+ will not need to pull down the DTD, as it will normally be embedded
+ in the implementation, and presents something of a catch-22 if you
+ need to load part of your network protocol over the network.
+
+7.1. IPoXML DTD
+
+ <!--
+ DTD for IP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ -->
+ <!--
+ DTD data types:
+
+ Digits [0..9]+
+
+ Precedence "NetworkControl | InternetworkControl |
+ CRITIC | FlashOverride | Flash | Immediate |
+ Priority | Routine"
+
+ IP4Addr "dotted-decimal" notation of [RFC1123]
+
+ Class [0..3]
+
+ Sec "Unclassified | Confidential | EFTO | MMMM | PROG |
+ Restricted | Secret | Top Secret | Reserved"
+
+ Compartments [0..65535]
+
+ Handling [0..65535]
+
+ TCC [0..16777216]
+
+ -->
+
+
+
+Kennedy Informational [Page 7]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ENTITY % Digits "CDATA">
+ <!ENTITY % Precedence "CDATA">
+ <!ENTITY % IP4Addr "CDATA">
+ <!ENTITY % Class "CDATA">
+ <!ENTITY % Sec "CDATA">
+ <!ENTITY % Compartments "CDATA">
+ <!ENTITY % Handling "CDATA">
+ <!ENTITY % TCC "CDATA">
+
+ <!ELEMENT ip (header, payload)>
+
+ <!ELEMENT header (version, tos, total.length, id, flags, offset, ttl,
+ protocol, checksum, source, destination, options,
+ padding)>
+ <!-- length of header in 32-bit words -->
+ <!ATTLIST header
+ length %Digits; #REQUIRED>
+
+ <!ELEMENT version EMPTY>
+ <!-- ip version. SHOULD be "4" -->
+ <!ATTLIST version
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT tos EMPTY>
+ <!ATTLIST tos
+ precedence %Precedence; #REQUIRED
+ delay (normal | low) #REQUIRED
+ throughput (normal | high) #REQUIRED
+ relibility (normal | high) #REQUIRED
+ reserved CDATA #FIXED "0">
+
+ <!ELEMENT total.length EMPTY>
+ <!--
+ total length of datagram (header and payload) in octets, MUST be
+ less than 65,535 (and SHOULD be less than 1024 for IPoXML on local
+ ethernets).
+ -->
+ <!ATTLIST total.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT id EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST id
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT flags EMPTY>
+ <!-- df = don't fragment, mf = more fragments -->
+ <!ATTLIST flags
+
+
+
+Kennedy Informational [Page 8]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ reserved CDATA #FIXED "0"
+ df (may|dont) #REQUIRED
+ mf (last|more) #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= offset <= 8192 measured in 8 octet (64-bit) chunks -->
+ <!ATTLIST offset
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT ttl EMPTY>
+ <!-- 0 <= ttl <= 255 -->
+ <!ATTLIST ttl
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT protocol EMPTY>
+ <!-- 0 <= protocol <= 255 (per IANA) -->
+ <!ATTLIST protocol
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT checksum EMPTY>
+ <!-- 0 <= checksum <= 65535 (over header only) -->
+ <!ATTLIST checksum
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT source EMPTY>
+ <!ATTLIST source
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT destination EMPTY>
+ <!ATTLIST destination
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT options ( end | noop | security | loose | strict | record
+ | stream | timestamp )*>
+
+ <!ELEMENT end EMPTY>
+ <!ATTLIST end
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "0">
+
+ <!ELEMENT noop EMPTY>
+ <!ATTLIST noop
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "1">
+
+ <!ELEMENT security EMPTY>
+
+
+
+Kennedy Informational [Page 9]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST security
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "2"
+ length CDATA #FIXED "11"
+ security %Sec; #REQUIRED
+ compartments %Compartments; #REQUIRED
+ handling %Handling; #REQUIRED
+ tcc %TCC; #REQUIRED>
+ <!ELEMENT loose (hop)+>
+ <!ATTLIST loose
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "3"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT hop EMPTY>
+ <!ATTLIST hop
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT strict (hop)+>
+ <!ATTLIST strict
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "9"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT record (hop)+>
+ <!ATTLIST record
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "7"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT stream EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST stream
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "8"
+ length CDATA #FIXED "4"
+ id %Digits; #REQUIRED>
+
+ <!ELEMENT timestamp (tstamp)+>
+ <!-- 0 <= oflw <=15 -->
+
+
+
+Kennedy Informational [Page 10]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST timestamp
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "2"
+ number CDATA #FIXED "4"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED
+ oflw %Digits; #REQUIRED
+ flag (0 | 1 | 3) #REQUIRED>
+
+ <!ELEMENT tstamp EMPTY>
+ <!ATTLIST tstamp
+ time %Digits; #REQUIRED
+ address %IP4Addr; #IMPLIED>
+ <!--
+ padding to bring header to 32-bit boundary.
+ pad MUST be "0"*
+ -->
+ <!ELEMENT padding EMPTY>
+ <!ATTLIST padding
+ pad CDATA #REQUIRED>
+
+ <!-- payload MUST be encoded as base-64 [RFC2045], as modified
+ by section 2.1 of this RFC -->
+ <!ELEMENT payload (CDATA)>
+
+7.2. TCPoXML DTD
+
+ <!--
+ DTD for TCP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ -->
+
+ <!-- the pseudoheader is only included for checksum calculations -->
+ <!ELEMENT tcp (tcp.pseudoheader?, tcp.header, payload)>
+
+ <!ELEMENT tcp.header (src, dest, sequence, acknowledgement, offset,
+ reserved, control, window, checksum, urgent,
+ tcp.options, padding)>
+
+ <!ELEMENT src EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+ <!ATTLIST src
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT dest EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+
+
+
+Kennedy Informational [Page 11]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST dest
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT sequence EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST sequence
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT acknowledgement EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST acknowledgement
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= number <= 255 -->
+ <!ATTLIST offset
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT reserved EMPTY>
+ <!ATTLIST reserved
+ value CDATA #FIXED "0">
+
+ <!ELEMENT control EMPTY>
+ <!ATTLIST control
+ urg (0|1) #IMPLIED
+ ack (0|1) #IMPLIED
+ psh (0|1) #IMPLIED
+ rst (0|1) #IMPLIED
+ syn (0|1) #IMPLIED
+ fin (0|1) #IMPLIED>
+
+ <!ELEMENT window EMPTY>
+ <!-- 0 <= size <= 65,535 -->
+ <!ATTLIST window
+ size %Digits; #REQUIRED>
+
+ <!--
+ checksum as in ip, but with
+ the following pseudo-header added into the tcp element:
+ -->
+ <!ELEMENT tcp.pseudoheader (source, destination, protocol,
+ tcp.length)>
+
+ <!--
+ tcp header + data length in octets. does not include the size of
+
+ the pseudoheader.
+ -->
+
+
+
+Kennedy Informational [Page 12]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ELEMENT tcp.length EMPTY>
+ <!ATTLIST tcp.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT urgent EMPTY>
+ <!-- 0 <= pointer <= 65,535 -->
+ <!ATTLIST urgent
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT tcp.options (tcp.end | tcp.noop | tcp.mss)+>
+
+ <!ELEMENT tcp.end EMPTY>
+ <!ATTLIST tcp.end
+ kind CDATA #FIXED "0">
+
+ <!ELEMENT tcp.noop EMPTY>
+ <!ATTLIST tcp.noop
+ kind CDATA #FIXED "1">
+
+ <!ELEMENT tcp.mss EMPTY>
+ <!ATTLIST tcp.mss
+ kind CDATA #FIXED "2"
+ length CDATA #FIXED "4"
+ size %Digits; #REQUIRED>
+
+7.3. UDPoXML DTD
+
+ <!--
+ DTD for UDP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ -->
+
+ <!ELEMENT udp (udp.pseudoheader?, udp.header, payload)>
+
+ <!ELEMENT udp.header (src, dest, udp.length, checksum)>
+
+ <!ELEMENT udp.pseudoheader (source, destination, protocol,
+ udp.length)>
+
+ <!--
+ udp header + data length in octets. does not include the size of
+ the pseudoheader.
+ -->
+ <!ELEMENT udp.length EMPTY>
+ <!ATTLIST udp.length
+ value %Digits; #REQUIRED>
+
+
+
+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:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ <ip>
+ <header length="474">
+ <version value="4"/>
+ <tos precedence="Routine" delay="Normal" throughput="Normal"
+ relibility="Normal" reserved="0"/>
+ <total.length value="461"/>
+ <id value="1"/>
+ <flags reserved="0" df="dont" mf="last"/>
+ <offset value="0"/>
+ <ttl value="255"/>
+ <protocol value="6"/>
+ <checksum value="8707"/>
+ <source address="10.0.0.22"/>
+ <destination address="10.0.0.1"/>
+ <options>
+ <end copied="0" class="0" number="0"/>
+ </options>
+ <padding pad="0"/>
+ </header>
+ <payload>
+ </payload>
+ </ip>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+3.2. Example Datagram
+
+ The following is an example TCPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ <tcp>
+ <tcp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <sequence number="322622954"/>
+ <acknowledgement number="689715995"/>
+ <offset number=""/>
+ <reserved value="0"/>
+ <control syn="1" ack="1"/>
+ <window size="1"/>
+ <urgent pointer="0"/>
+ <checksum value="2988"/>
+ <tcp.options>
+ <tcp.end kind="0"/>
+ </tcp.options>
+ <padding pad="0"/>
+ </tcp.header>
+ <payload>
+ </payload>
+ </tcp>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+4.2. Example Datagram
+
+ The following is an example UDPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ <udp>
+ <udp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <udp.length value="143"/>
+ <checksum value="2988"/>
+ </udp.header>
+ <payload>
+ </payload>
+ </udp>
+
+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 ("<?xml".)
+
+5.2. IEEE 802
+
+ BLOAT is encapsulated in IEEE 802 Networks as in [RFC1042] except
+ that the protocol type code for IPoXML is 0xBEEF.
+
+
+
+
+
+Kennedy Informational [Page 6]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+6. Gatewaying over IP
+
+ In order to facilitate the gradual introduction of BLOAT into the
+ public Internet, BLOAT MAY be encapsulated in IP as in [RFC2003] to
+ gateway between networks that run BLOAT natively on their LANs.
+
+7. DTDs
+
+ The Transport DTDs (7.2. and 7.3.) build on the definitions in the
+ Network DTD (7.1.)
+
+ The DTDs are referenced by their PubidLiteral and SystemLiteral (from
+ [XML]) although it is understood that most IPoXML implementations
+ will not need to pull down the DTD, as it will normally be embedded
+ in the implementation, and presents something of a catch-22 if you
+ need to load part of your network protocol over the network.
+
+7.1. IPoXML DTD
+
+ <!--
+ DTD for IP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ -->
+ <!--
+ DTD data types:
+
+ Digits [0..9]+
+
+ Precedence "NetworkControl | InternetworkControl |
+ CRITIC | FlashOverride | Flash | Immediate |
+ Priority | Routine"
+
+ IP4Addr "dotted-decimal" notation of [RFC1123]
+
+ Class [0..3]
+
+ Sec "Unclassified | Confidential | EFTO | MMMM | PROG |
+ Restricted | Secret | Top Secret | Reserved"
+
+ Compartments [0..65535]
+
+ Handling [0..65535]
+
+ TCC [0..16777216]
+
+ -->
+
+
+
+Kennedy Informational [Page 7]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ENTITY % Digits "CDATA">
+ <!ENTITY % Precedence "CDATA">
+ <!ENTITY % IP4Addr "CDATA">
+ <!ENTITY % Class "CDATA">
+ <!ENTITY % Sec "CDATA">
+ <!ENTITY % Compartments "CDATA">
+ <!ENTITY % Handling "CDATA">
+ <!ENTITY % TCC "CDATA">
+
+ <!ELEMENT ip (header, payload)>
+
+ <!ELEMENT header (version, tos, total.length, id, flags, offset, ttl,
+ protocol, checksum, source, destination, options,
+ padding)>
+ <!-- length of header in 32-bit words -->
+ <!ATTLIST header
+ length %Digits; #REQUIRED>
+
+ <!ELEMENT version EMPTY>
+ <!-- ip version. SHOULD be "4" -->
+ <!ATTLIST version
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT tos EMPTY>
+ <!ATTLIST tos
+ precedence %Precedence; #REQUIRED
+ delay (normal | low) #REQUIRED
+ throughput (normal | high) #REQUIRED
+ relibility (normal | high) #REQUIRED
+ reserved CDATA #FIXED "0">
+
+ <!ELEMENT total.length EMPTY>
+ <!--
+ total length of datagram (header and payload) in octets, MUST be
+ less than 65,535 (and SHOULD be less than 1024 for IPoXML on local
+ ethernets).
+ -->
+ <!ATTLIST total.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT id EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST id
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT flags EMPTY>
+ <!-- df = don't fragment, mf = more fragments -->
+ <!ATTLIST flags
+
+
+
+Kennedy Informational [Page 8]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ reserved CDATA #FIXED "0"
+ df (may|dont) #REQUIRED
+ mf (last|more) #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= offset <= 8192 measured in 8 octet (64-bit) chunks -->
+ <!ATTLIST offset
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT ttl EMPTY>
+ <!-- 0 <= ttl <= 255 -->
+ <!ATTLIST ttl
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT protocol EMPTY>
+ <!-- 0 <= protocol <= 255 (per IANA) -->
+ <!ATTLIST protocol
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT checksum EMPTY>
+ <!-- 0 <= checksum <= 65535 (over header only) -->
+ <!ATTLIST checksum
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT source EMPTY>
+ <!ATTLIST source
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT destination EMPTY>
+ <!ATTLIST destination
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT options ( end | noop | security | loose | strict | record
+ | stream | timestamp )*>
+
+ <!ELEMENT end EMPTY>
+ <!ATTLIST end
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "0">
+
+ <!ELEMENT noop EMPTY>
+ <!ATTLIST noop
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "1">
+
+ <!ELEMENT security EMPTY>
+
+
+
+Kennedy Informational [Page 9]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST security
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "2"
+ length CDATA #FIXED "11"
+ security %Sec; #REQUIRED
+ compartments %Compartments; #REQUIRED
+ handling %Handling; #REQUIRED
+ tcc %TCC; #REQUIRED>
+ <!ELEMENT loose (hop)+>
+ <!ATTLIST loose
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "3"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT hop EMPTY>
+ <!ATTLIST hop
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT strict (hop)+>
+ <!ATTLIST strict
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "9"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT record (hop)+>
+ <!ATTLIST record
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "7"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT stream EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST stream
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "8"
+ length CDATA #FIXED "4"
+ id %Digits; #REQUIRED>
+
+ <!ELEMENT timestamp (tstamp)+>
+ <!-- 0 <= oflw <=15 -->
+
+
+
+Kennedy Informational [Page 10]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST timestamp
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "2"
+ number CDATA #FIXED "4"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED
+ oflw %Digits; #REQUIRED
+ flag (0 | 1 | 3) #REQUIRED>
+
+ <!ELEMENT tstamp EMPTY>
+ <!ATTLIST tstamp
+ time %Digits; #REQUIRED
+ address %IP4Addr; #IMPLIED>
+ <!--
+ padding to bring header to 32-bit boundary.
+ pad MUST be "0"*
+ -->
+ <!ELEMENT padding EMPTY>
+ <!ATTLIST padding
+ pad CDATA #REQUIRED>
+
+ <!-- payload MUST be encoded as base-64 [RFC2045], as modified
+ by section 2.1 of this RFC -->
+ <!ELEMENT payload (CDATA)>
+
+7.2. TCPoXML DTD
+
+ <!--
+ DTD for TCP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ -->
+
+ <!-- the pseudoheader is only included for checksum calculations -->
+ <!ELEMENT tcp (tcp.pseudoheader?, tcp.header, payload)>
+
+ <!ELEMENT tcp.header (src, dest, sequence, acknowledgement, offset,
+ reserved, control, window, checksum, urgent,
+ tcp.options, padding)>
+
+ <!ELEMENT src EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+ <!ATTLIST src
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT dest EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+
+
+
+Kennedy Informational [Page 11]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST dest
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT sequence EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST sequence
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT acknowledgement EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST acknowledgement
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= number <= 255 -->
+ <!ATTLIST offset
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT reserved EMPTY>
+ <!ATTLIST reserved
+ value CDATA #FIXED "0">
+
+ <!ELEMENT control EMPTY>
+ <!ATTLIST control
+ urg (0|1) #IMPLIED
+ ack (0|1) #IMPLIED
+ psh (0|1) #IMPLIED
+ rst (0|1) #IMPLIED
+ syn (0|1) #IMPLIED
+ fin (0|1) #IMPLIED>
+
+ <!ELEMENT window EMPTY>
+ <!-- 0 <= size <= 65,535 -->
+ <!ATTLIST window
+ size %Digits; #REQUIRED>
+
+ <!--
+ checksum as in ip, but with
+ the following pseudo-header added into the tcp element:
+ -->
+ <!ELEMENT tcp.pseudoheader (source, destination, protocol,
+ tcp.length)>
+
+ <!--
+ tcp header + data length in octets. does not include the size of
+
+ the pseudoheader.
+ -->
+
+
+
+Kennedy Informational [Page 12]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ELEMENT tcp.length EMPTY>
+ <!ATTLIST tcp.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT urgent EMPTY>
+ <!-- 0 <= pointer <= 65,535 -->
+ <!ATTLIST urgent
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT tcp.options (tcp.end | tcp.noop | tcp.mss)+>
+
+ <!ELEMENT tcp.end EMPTY>
+ <!ATTLIST tcp.end
+ kind CDATA #FIXED "0">
+
+ <!ELEMENT tcp.noop EMPTY>
+ <!ATTLIST tcp.noop
+ kind CDATA #FIXED "1">
+
+ <!ELEMENT tcp.mss EMPTY>
+ <!ATTLIST tcp.mss
+ kind CDATA #FIXED "2"
+ length CDATA #FIXED "4"
+ size %Digits; #REQUIRED>
+
+7.3. UDPoXML DTD
+
+ <!--
+ DTD for UDP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ -->
+
+ <!ELEMENT udp (udp.pseudoheader?, udp.header, payload)>
+
+ <!ELEMENT udp.header (src, dest, udp.length, checksum)>
+
+ <!ELEMENT udp.pseudoheader (source, destination, protocol,
+ udp.length)>
+
+ <!--
+ udp header + data length in octets. does not include the size of
+ the pseudoheader.
+ -->
+ <!ELEMENT udp.length EMPTY>
+ <!ATTLIST udp.length
+ value %Digits; #REQUIRED>
+
+
+
+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:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ <ip>
+ <header length="474">
+ <version value="4"/>
+ <tos precedence="Routine" delay="Normal" throughput="Normal"
+ relibility="Normal" reserved="0"/>
+ <total.length value="461"/>
+ <id value="1"/>
+ <flags reserved="0" df="dont" mf="last"/>
+ <offset value="0"/>
+ <ttl value="255"/>
+ <protocol value="6"/>
+ <checksum value="8707"/>
+ <source address="10.0.0.22"/>
+ <destination address="10.0.0.1"/>
+ <options>
+ <end copied="0" class="0" number="0"/>
+ </options>
+ <padding pad="0"/>
+ </header>
+ <payload>
+ </payload>
+ </ip>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+3.2. Example Datagram
+
+ The following is an example TCPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ <tcp>
+ <tcp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <sequence number="322622954"/>
+ <acknowledgement number="689715995"/>
+ <offset number=""/>
+ <reserved value="0"/>
+ <control syn="1" ack="1"/>
+ <window size="1"/>
+ <urgent pointer="0"/>
+ <checksum value="2988"/>
+ <tcp.options>
+ <tcp.end kind="0"/>
+ </tcp.options>
+ <padding pad="0"/>
+ </tcp.header>
+ <payload>
+ </payload>
+ </tcp>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+4.2. Example Datagram
+
+ The following is an example UDPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ <udp>
+ <udp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <udp.length value="143"/>
+ <checksum value="2988"/>
+ </udp.header>
+ <payload>
+ </payload>
+ </udp>
+
+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 ("<?xml".)
+
+5.2. IEEE 802
+
+ BLOAT is encapsulated in IEEE 802 Networks as in [RFC1042] except
+ that the protocol type code for IPoXML is 0xBEEF.
+
+
+
+
+
+Kennedy Informational [Page 6]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+6. Gatewaying over IP
+
+ In order to facilitate the gradual introduction of BLOAT into the
+ public Internet, BLOAT MAY be encapsulated in IP as in [RFC2003] to
+ gateway between networks that run BLOAT natively on their LANs.
+
+7. DTDs
+
+ The Transport DTDs (7.2. and 7.3.) build on the definitions in the
+ Network DTD (7.1.)
+
+ The DTDs are referenced by their PubidLiteral and SystemLiteral (from
+ [XML]) although it is understood that most IPoXML implementations
+ will not need to pull down the DTD, as it will normally be embedded
+ in the implementation, and presents something of a catch-22 if you
+ need to load part of your network protocol over the network.
+
+7.1. IPoXML DTD
+
+ <!--
+ DTD for IP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ -->
+ <!--
+ DTD data types:
+
+ Digits [0..9]+
+
+ Precedence "NetworkControl | InternetworkControl |
+ CRITIC | FlashOverride | Flash | Immediate |
+ Priority | Routine"
+
+ IP4Addr "dotted-decimal" notation of [RFC1123]
+
+ Class [0..3]
+
+ Sec "Unclassified | Confidential | EFTO | MMMM | PROG |
+ Restricted | Secret | Top Secret | Reserved"
+
+ Compartments [0..65535]
+
+ Handling [0..65535]
+
+ TCC [0..16777216]
+
+ -->
+
+
+
+Kennedy Informational [Page 7]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ENTITY % Digits "CDATA">
+ <!ENTITY % Precedence "CDATA">
+ <!ENTITY % IP4Addr "CDATA">
+ <!ENTITY % Class "CDATA">
+ <!ENTITY % Sec "CDATA">
+ <!ENTITY % Compartments "CDATA">
+ <!ENTITY % Handling "CDATA">
+ <!ENTITY % TCC "CDATA">
+
+ <!ELEMENT ip (header, payload)>
+
+ <!ELEMENT header (version, tos, total.length, id, flags, offset, ttl,
+ protocol, checksum, source, destination, options,
+ padding)>
+ <!-- length of header in 32-bit words -->
+ <!ATTLIST header
+ length %Digits; #REQUIRED>
+
+ <!ELEMENT version EMPTY>
+ <!-- ip version. SHOULD be "4" -->
+ <!ATTLIST version
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT tos EMPTY>
+ <!ATTLIST tos
+ precedence %Precedence; #REQUIRED
+ delay (normal | low) #REQUIRED
+ throughput (normal | high) #REQUIRED
+ relibility (normal | high) #REQUIRED
+ reserved CDATA #FIXED "0">
+
+ <!ELEMENT total.length EMPTY>
+ <!--
+ total length of datagram (header and payload) in octets, MUST be
+ less than 65,535 (and SHOULD be less than 1024 for IPoXML on local
+ ethernets).
+ -->
+ <!ATTLIST total.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT id EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST id
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT flags EMPTY>
+ <!-- df = don't fragment, mf = more fragments -->
+ <!ATTLIST flags
+
+
+
+Kennedy Informational [Page 8]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ reserved CDATA #FIXED "0"
+ df (may|dont) #REQUIRED
+ mf (last|more) #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= offset <= 8192 measured in 8 octet (64-bit) chunks -->
+ <!ATTLIST offset
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT ttl EMPTY>
+ <!-- 0 <= ttl <= 255 -->
+ <!ATTLIST ttl
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT protocol EMPTY>
+ <!-- 0 <= protocol <= 255 (per IANA) -->
+ <!ATTLIST protocol
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT checksum EMPTY>
+ <!-- 0 <= checksum <= 65535 (over header only) -->
+ <!ATTLIST checksum
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT source EMPTY>
+ <!ATTLIST source
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT destination EMPTY>
+ <!ATTLIST destination
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT options ( end | noop | security | loose | strict | record
+ | stream | timestamp )*>
+
+ <!ELEMENT end EMPTY>
+ <!ATTLIST end
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "0">
+
+ <!ELEMENT noop EMPTY>
+ <!ATTLIST noop
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "1">
+
+ <!ELEMENT security EMPTY>
+
+
+
+Kennedy Informational [Page 9]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST security
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "2"
+ length CDATA #FIXED "11"
+ security %Sec; #REQUIRED
+ compartments %Compartments; #REQUIRED
+ handling %Handling; #REQUIRED
+ tcc %TCC; #REQUIRED>
+ <!ELEMENT loose (hop)+>
+ <!ATTLIST loose
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "3"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT hop EMPTY>
+ <!ATTLIST hop
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT strict (hop)+>
+ <!ATTLIST strict
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "9"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT record (hop)+>
+ <!ATTLIST record
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "7"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT stream EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST stream
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "8"
+ length CDATA #FIXED "4"
+ id %Digits; #REQUIRED>
+
+ <!ELEMENT timestamp (tstamp)+>
+ <!-- 0 <= oflw <=15 -->
+
+
+
+Kennedy Informational [Page 10]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST timestamp
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "2"
+ number CDATA #FIXED "4"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED
+ oflw %Digits; #REQUIRED
+ flag (0 | 1 | 3) #REQUIRED>
+
+ <!ELEMENT tstamp EMPTY>
+ <!ATTLIST tstamp
+ time %Digits; #REQUIRED
+ address %IP4Addr; #IMPLIED>
+ <!--
+ padding to bring header to 32-bit boundary.
+ pad MUST be "0"*
+ -->
+ <!ELEMENT padding EMPTY>
+ <!ATTLIST padding
+ pad CDATA #REQUIRED>
+
+ <!-- payload MUST be encoded as base-64 [RFC2045], as modified
+ by section 2.1 of this RFC -->
+ <!ELEMENT payload (CDATA)>
+
+7.2. TCPoXML DTD
+
+ <!--
+ DTD for TCP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ -->
+
+ <!-- the pseudoheader is only included for checksum calculations -->
+ <!ELEMENT tcp (tcp.pseudoheader?, tcp.header, payload)>
+
+ <!ELEMENT tcp.header (src, dest, sequence, acknowledgement, offset,
+ reserved, control, window, checksum, urgent,
+ tcp.options, padding)>
+
+ <!ELEMENT src EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+ <!ATTLIST src
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT dest EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+
+
+
+Kennedy Informational [Page 11]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST dest
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT sequence EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST sequence
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT acknowledgement EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST acknowledgement
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= number <= 255 -->
+ <!ATTLIST offset
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT reserved EMPTY>
+ <!ATTLIST reserved
+ value CDATA #FIXED "0">
+
+ <!ELEMENT control EMPTY>
+ <!ATTLIST control
+ urg (0|1) #IMPLIED
+ ack (0|1) #IMPLIED
+ psh (0|1) #IMPLIED
+ rst (0|1) #IMPLIED
+ syn (0|1) #IMPLIED
+ fin (0|1) #IMPLIED>
+
+ <!ELEMENT window EMPTY>
+ <!-- 0 <= size <= 65,535 -->
+ <!ATTLIST window
+ size %Digits; #REQUIRED>
+
+ <!--
+ checksum as in ip, but with
+ the following pseudo-header added into the tcp element:
+ -->
+ <!ELEMENT tcp.pseudoheader (source, destination, protocol,
+ tcp.length)>
+
+ <!--
+ tcp header + data length in octets. does not include the size of
+
+ the pseudoheader.
+ -->
+
+
+
+Kennedy Informational [Page 12]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ELEMENT tcp.length EMPTY>
+ <!ATTLIST tcp.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT urgent EMPTY>
+ <!-- 0 <= pointer <= 65,535 -->
+ <!ATTLIST urgent
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT tcp.options (tcp.end | tcp.noop | tcp.mss)+>
+
+ <!ELEMENT tcp.end EMPTY>
+ <!ATTLIST tcp.end
+ kind CDATA #FIXED "0">
+
+ <!ELEMENT tcp.noop EMPTY>
+ <!ATTLIST tcp.noop
+ kind CDATA #FIXED "1">
+
+ <!ELEMENT tcp.mss EMPTY>
+ <!ATTLIST tcp.mss
+ kind CDATA #FIXED "2"
+ length CDATA #FIXED "4"
+ size %Digits; #REQUIRED>
+
+7.3. UDPoXML DTD
+
+ <!--
+ DTD for UDP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ -->
+
+ <!ELEMENT udp (udp.pseudoheader?, udp.header, payload)>
+
+ <!ELEMENT udp.header (src, dest, udp.length, checksum)>
+
+ <!ELEMENT udp.pseudoheader (source, destination, protocol,
+ udp.length)>
+
+ <!--
+ udp header + data length in octets. does not include the size of
+ the pseudoheader.
+ -->
+ <!ELEMENT udp.length EMPTY>
+ <!ATTLIST udp.length
+ value %Digits; #REQUIRED>
+
+
+
+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:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ <ip>
+ <header length="474">
+ <version value="4"/>
+ <tos precedence="Routine" delay="Normal" throughput="Normal"
+ relibility="Normal" reserved="0"/>
+ <total.length value="461"/>
+ <id value="1"/>
+ <flags reserved="0" df="dont" mf="last"/>
+ <offset value="0"/>
+ <ttl value="255"/>
+ <protocol value="6"/>
+ <checksum value="8707"/>
+ <source address="10.0.0.22"/>
+ <destination address="10.0.0.1"/>
+ <options>
+ <end copied="0" class="0" number="0"/>
+ </options>
+ <padding pad="0"/>
+ </header>
+ <payload>
+ </payload>
+ </ip>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+3.2. Example Datagram
+
+ The following is an example TCPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ <tcp>
+ <tcp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <sequence number="322622954"/>
+ <acknowledgement number="689715995"/>
+ <offset number=""/>
+ <reserved value="0"/>
+ <control syn="1" ack="1"/>
+ <window size="1"/>
+ <urgent pointer="0"/>
+ <checksum value="2988"/>
+ <tcp.options>
+ <tcp.end kind="0"/>
+ </tcp.options>
+ <padding pad="0"/>
+ </tcp.header>
+ <payload>
+ </payload>
+ </tcp>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+4.2. Example Datagram
+
+ The following is an example UDPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ <udp>
+ <udp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <udp.length value="143"/>
+ <checksum value="2988"/>
+ </udp.header>
+ <payload>
+ </payload>
+ </udp>
+
+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 ("<?xml".)
+
+5.2. IEEE 802
+
+ BLOAT is encapsulated in IEEE 802 Networks as in [RFC1042] except
+ that the protocol type code for IPoXML is 0xBEEF.
+
+
+
+
+
+Kennedy Informational [Page 6]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+6. Gatewaying over IP
+
+ In order to facilitate the gradual introduction of BLOAT into the
+ public Internet, BLOAT MAY be encapsulated in IP as in [RFC2003] to
+ gateway between networks that run BLOAT natively on their LANs.
+
+7. DTDs
+
+ The Transport DTDs (7.2. and 7.3.) build on the definitions in the
+ Network DTD (7.1.)
+
+ The DTDs are referenced by their PubidLiteral and SystemLiteral (from
+ [XML]) although it is understood that most IPoXML implementations
+ will not need to pull down the DTD, as it will normally be embedded
+ in the implementation, and presents something of a catch-22 if you
+ need to load part of your network protocol over the network.
+
+7.1. IPoXML DTD
+
+ <!--
+ DTD for IP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ -->
+ <!--
+ DTD data types:
+
+ Digits [0..9]+
+
+ Precedence "NetworkControl | InternetworkControl |
+ CRITIC | FlashOverride | Flash | Immediate |
+ Priority | Routine"
+
+ IP4Addr "dotted-decimal" notation of [RFC1123]
+
+ Class [0..3]
+
+ Sec "Unclassified | Confidential | EFTO | MMMM | PROG |
+ Restricted | Secret | Top Secret | Reserved"
+
+ Compartments [0..65535]
+
+ Handling [0..65535]
+
+ TCC [0..16777216]
+
+ -->
+
+
+
+Kennedy Informational [Page 7]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ENTITY % Digits "CDATA">
+ <!ENTITY % Precedence "CDATA">
+ <!ENTITY % IP4Addr "CDATA">
+ <!ENTITY % Class "CDATA">
+ <!ENTITY % Sec "CDATA">
+ <!ENTITY % Compartments "CDATA">
+ <!ENTITY % Handling "CDATA">
+ <!ENTITY % TCC "CDATA">
+
+ <!ELEMENT ip (header, payload)>
+
+ <!ELEMENT header (version, tos, total.length, id, flags, offset, ttl,
+ protocol, checksum, source, destination, options,
+ padding)>
+ <!-- length of header in 32-bit words -->
+ <!ATTLIST header
+ length %Digits; #REQUIRED>
+
+ <!ELEMENT version EMPTY>
+ <!-- ip version. SHOULD be "4" -->
+ <!ATTLIST version
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT tos EMPTY>
+ <!ATTLIST tos
+ precedence %Precedence; #REQUIRED
+ delay (normal | low) #REQUIRED
+ throughput (normal | high) #REQUIRED
+ relibility (normal | high) #REQUIRED
+ reserved CDATA #FIXED "0">
+
+ <!ELEMENT total.length EMPTY>
+ <!--
+ total length of datagram (header and payload) in octets, MUST be
+ less than 65,535 (and SHOULD be less than 1024 for IPoXML on local
+ ethernets).
+ -->
+ <!ATTLIST total.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT id EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST id
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT flags EMPTY>
+ <!-- df = don't fragment, mf = more fragments -->
+ <!ATTLIST flags
+
+
+
+Kennedy Informational [Page 8]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ reserved CDATA #FIXED "0"
+ df (may|dont) #REQUIRED
+ mf (last|more) #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= offset <= 8192 measured in 8 octet (64-bit) chunks -->
+ <!ATTLIST offset
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT ttl EMPTY>
+ <!-- 0 <= ttl <= 255 -->
+ <!ATTLIST ttl
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT protocol EMPTY>
+ <!-- 0 <= protocol <= 255 (per IANA) -->
+ <!ATTLIST protocol
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT checksum EMPTY>
+ <!-- 0 <= checksum <= 65535 (over header only) -->
+ <!ATTLIST checksum
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT source EMPTY>
+ <!ATTLIST source
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT destination EMPTY>
+ <!ATTLIST destination
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT options ( end | noop | security | loose | strict | record
+ | stream | timestamp )*>
+
+ <!ELEMENT end EMPTY>
+ <!ATTLIST end
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "0">
+
+ <!ELEMENT noop EMPTY>
+ <!ATTLIST noop
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "1">
+
+ <!ELEMENT security EMPTY>
+
+
+
+Kennedy Informational [Page 9]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST security
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "2"
+ length CDATA #FIXED "11"
+ security %Sec; #REQUIRED
+ compartments %Compartments; #REQUIRED
+ handling %Handling; #REQUIRED
+ tcc %TCC; #REQUIRED>
+ <!ELEMENT loose (hop)+>
+ <!ATTLIST loose
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "3"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT hop EMPTY>
+ <!ATTLIST hop
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT strict (hop)+>
+ <!ATTLIST strict
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "9"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT record (hop)+>
+ <!ATTLIST record
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "7"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT stream EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST stream
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "8"
+ length CDATA #FIXED "4"
+ id %Digits; #REQUIRED>
+
+ <!ELEMENT timestamp (tstamp)+>
+ <!-- 0 <= oflw <=15 -->
+
+
+
+Kennedy Informational [Page 10]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST timestamp
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "2"
+ number CDATA #FIXED "4"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED
+ oflw %Digits; #REQUIRED
+ flag (0 | 1 | 3) #REQUIRED>
+
+ <!ELEMENT tstamp EMPTY>
+ <!ATTLIST tstamp
+ time %Digits; #REQUIRED
+ address %IP4Addr; #IMPLIED>
+ <!--
+ padding to bring header to 32-bit boundary.
+ pad MUST be "0"*
+ -->
+ <!ELEMENT padding EMPTY>
+ <!ATTLIST padding
+ pad CDATA #REQUIRED>
+
+ <!-- payload MUST be encoded as base-64 [RFC2045], as modified
+ by section 2.1 of this RFC -->
+ <!ELEMENT payload (CDATA)>
+
+7.2. TCPoXML DTD
+
+ <!--
+ DTD for TCP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ -->
+
+ <!-- the pseudoheader is only included for checksum calculations -->
+ <!ELEMENT tcp (tcp.pseudoheader?, tcp.header, payload)>
+
+ <!ELEMENT tcp.header (src, dest, sequence, acknowledgement, offset,
+ reserved, control, window, checksum, urgent,
+ tcp.options, padding)>
+
+ <!ELEMENT src EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+ <!ATTLIST src
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT dest EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+
+
+
+Kennedy Informational [Page 11]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST dest
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT sequence EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST sequence
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT acknowledgement EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST acknowledgement
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= number <= 255 -->
+ <!ATTLIST offset
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT reserved EMPTY>
+ <!ATTLIST reserved
+ value CDATA #FIXED "0">
+
+ <!ELEMENT control EMPTY>
+ <!ATTLIST control
+ urg (0|1) #IMPLIED
+ ack (0|1) #IMPLIED
+ psh (0|1) #IMPLIED
+ rst (0|1) #IMPLIED
+ syn (0|1) #IMPLIED
+ fin (0|1) #IMPLIED>
+
+ <!ELEMENT window EMPTY>
+ <!-- 0 <= size <= 65,535 -->
+ <!ATTLIST window
+ size %Digits; #REQUIRED>
+
+ <!--
+ checksum as in ip, but with
+ the following pseudo-header added into the tcp element:
+ -->
+ <!ELEMENT tcp.pseudoheader (source, destination, protocol,
+ tcp.length)>
+
+ <!--
+ tcp header + data length in octets. does not include the size of
+
+ the pseudoheader.
+ -->
+
+
+
+Kennedy Informational [Page 12]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ELEMENT tcp.length EMPTY>
+ <!ATTLIST tcp.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT urgent EMPTY>
+ <!-- 0 <= pointer <= 65,535 -->
+ <!ATTLIST urgent
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT tcp.options (tcp.end | tcp.noop | tcp.mss)+>
+
+ <!ELEMENT tcp.end EMPTY>
+ <!ATTLIST tcp.end
+ kind CDATA #FIXED "0">
+
+ <!ELEMENT tcp.noop EMPTY>
+ <!ATTLIST tcp.noop
+ kind CDATA #FIXED "1">
+
+ <!ELEMENT tcp.mss EMPTY>
+ <!ATTLIST tcp.mss
+ kind CDATA #FIXED "2"
+ length CDATA #FIXED "4"
+ size %Digits; #REQUIRED>
+
+7.3. UDPoXML DTD
+
+ <!--
+ DTD for UDP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ -->
+
+ <!ELEMENT udp (udp.pseudoheader?, udp.header, payload)>
+
+ <!ELEMENT udp.header (src, dest, udp.length, checksum)>
+
+ <!ELEMENT udp.pseudoheader (source, destination, protocol,
+ udp.length)>
+
+ <!--
+ udp header + data length in octets. does not include the size of
+ the pseudoheader.
+ -->
+ <!ELEMENT udp.length EMPTY>
+ <!ATTLIST udp.length
+ value %Digits; #REQUIRED>
+
+
+
+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:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ <ip>
+ <header length="474">
+ <version value="4"/>
+ <tos precedence="Routine" delay="Normal" throughput="Normal"
+ relibility="Normal" reserved="0"/>
+ <total.length value="461"/>
+ <id value="1"/>
+ <flags reserved="0" df="dont" mf="last"/>
+ <offset value="0"/>
+ <ttl value="255"/>
+ <protocol value="6"/>
+ <checksum value="8707"/>
+ <source address="10.0.0.22"/>
+ <destination address="10.0.0.1"/>
+ <options>
+ <end copied="0" class="0" number="0"/>
+ </options>
+ <padding pad="0"/>
+ </header>
+ <payload>
+ </payload>
+ </ip>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+3.2. Example Datagram
+
+ The following is an example TCPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ <tcp>
+ <tcp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <sequence number="322622954"/>
+ <acknowledgement number="689715995"/>
+ <offset number=""/>
+ <reserved value="0"/>
+ <control syn="1" ack="1"/>
+ <window size="1"/>
+ <urgent pointer="0"/>
+ <checksum value="2988"/>
+ <tcp.options>
+ <tcp.end kind="0"/>
+ </tcp.options>
+ <padding pad="0"/>
+ </tcp.header>
+ <payload>
+ </payload>
+ </tcp>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+4.2. Example Datagram
+
+ The following is an example UDPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ <udp>
+ <udp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <udp.length value="143"/>
+ <checksum value="2988"/>
+ </udp.header>
+ <payload>
+ </payload>
+ </udp>
+
+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 ("<?xml".)
+
+5.2. IEEE 802
+
+ BLOAT is encapsulated in IEEE 802 Networks as in [RFC1042] except
+ that the protocol type code for IPoXML is 0xBEEF.
+
+
+
+
+
+Kennedy Informational [Page 6]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+6. Gatewaying over IP
+
+ In order to facilitate the gradual introduction of BLOAT into the
+ public Internet, BLOAT MAY be encapsulated in IP as in [RFC2003] to
+ gateway between networks that run BLOAT natively on their LANs.
+
+7. DTDs
+
+ The Transport DTDs (7.2. and 7.3.) build on the definitions in the
+ Network DTD (7.1.)
+
+ The DTDs are referenced by their PubidLiteral and SystemLiteral (from
+ [XML]) although it is understood that most IPoXML implementations
+ will not need to pull down the DTD, as it will normally be embedded
+ in the implementation, and presents something of a catch-22 if you
+ need to load part of your network protocol over the network.
+
+7.1. IPoXML DTD
+
+ <!--
+ DTD for IP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ -->
+ <!--
+ DTD data types:
+
+ Digits [0..9]+
+
+ Precedence "NetworkControl | InternetworkControl |
+ CRITIC | FlashOverride | Flash | Immediate |
+ Priority | Routine"
+
+ IP4Addr "dotted-decimal" notation of [RFC1123]
+
+ Class [0..3]
+
+ Sec "Unclassified | Confidential | EFTO | MMMM | PROG |
+ Restricted | Secret | Top Secret | Reserved"
+
+ Compartments [0..65535]
+
+ Handling [0..65535]
+
+ TCC [0..16777216]
+
+ -->
+
+
+
+Kennedy Informational [Page 7]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ENTITY % Digits "CDATA">
+ <!ENTITY % Precedence "CDATA">
+ <!ENTITY % IP4Addr "CDATA">
+ <!ENTITY % Class "CDATA">
+ <!ENTITY % Sec "CDATA">
+ <!ENTITY % Compartments "CDATA">
+ <!ENTITY % Handling "CDATA">
+ <!ENTITY % TCC "CDATA">
+
+ <!ELEMENT ip (header, payload)>
+
+ <!ELEMENT header (version, tos, total.length, id, flags, offset, ttl,
+ protocol, checksum, source, destination, options,
+ padding)>
+ <!-- length of header in 32-bit words -->
+ <!ATTLIST header
+ length %Digits; #REQUIRED>
+
+ <!ELEMENT version EMPTY>
+ <!-- ip version. SHOULD be "4" -->
+ <!ATTLIST version
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT tos EMPTY>
+ <!ATTLIST tos
+ precedence %Precedence; #REQUIRED
+ delay (normal | low) #REQUIRED
+ throughput (normal | high) #REQUIRED
+ relibility (normal | high) #REQUIRED
+ reserved CDATA #FIXED "0">
+
+ <!ELEMENT total.length EMPTY>
+ <!--
+ total length of datagram (header and payload) in octets, MUST be
+ less than 65,535 (and SHOULD be less than 1024 for IPoXML on local
+ ethernets).
+ -->
+ <!ATTLIST total.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT id EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST id
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT flags EMPTY>
+ <!-- df = don't fragment, mf = more fragments -->
+ <!ATTLIST flags
+
+
+
+Kennedy Informational [Page 8]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ reserved CDATA #FIXED "0"
+ df (may|dont) #REQUIRED
+ mf (last|more) #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= offset <= 8192 measured in 8 octet (64-bit) chunks -->
+ <!ATTLIST offset
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT ttl EMPTY>
+ <!-- 0 <= ttl <= 255 -->
+ <!ATTLIST ttl
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT protocol EMPTY>
+ <!-- 0 <= protocol <= 255 (per IANA) -->
+ <!ATTLIST protocol
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT checksum EMPTY>
+ <!-- 0 <= checksum <= 65535 (over header only) -->
+ <!ATTLIST checksum
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT source EMPTY>
+ <!ATTLIST source
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT destination EMPTY>
+ <!ATTLIST destination
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT options ( end | noop | security | loose | strict | record
+ | stream | timestamp )*>
+
+ <!ELEMENT end EMPTY>
+ <!ATTLIST end
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "0">
+
+ <!ELEMENT noop EMPTY>
+ <!ATTLIST noop
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "1">
+
+ <!ELEMENT security EMPTY>
+
+
+
+Kennedy Informational [Page 9]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST security
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "2"
+ length CDATA #FIXED "11"
+ security %Sec; #REQUIRED
+ compartments %Compartments; #REQUIRED
+ handling %Handling; #REQUIRED
+ tcc %TCC; #REQUIRED>
+ <!ELEMENT loose (hop)+>
+ <!ATTLIST loose
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "3"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT hop EMPTY>
+ <!ATTLIST hop
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT strict (hop)+>
+ <!ATTLIST strict
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "9"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT record (hop)+>
+ <!ATTLIST record
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "7"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT stream EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST stream
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "8"
+ length CDATA #FIXED "4"
+ id %Digits; #REQUIRED>
+
+ <!ELEMENT timestamp (tstamp)+>
+ <!-- 0 <= oflw <=15 -->
+
+
+
+Kennedy Informational [Page 10]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST timestamp
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "2"
+ number CDATA #FIXED "4"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED
+ oflw %Digits; #REQUIRED
+ flag (0 | 1 | 3) #REQUIRED>
+
+ <!ELEMENT tstamp EMPTY>
+ <!ATTLIST tstamp
+ time %Digits; #REQUIRED
+ address %IP4Addr; #IMPLIED>
+ <!--
+ padding to bring header to 32-bit boundary.
+ pad MUST be "0"*
+ -->
+ <!ELEMENT padding EMPTY>
+ <!ATTLIST padding
+ pad CDATA #REQUIRED>
+
+ <!-- payload MUST be encoded as base-64 [RFC2045], as modified
+ by section 2.1 of this RFC -->
+ <!ELEMENT payload (CDATA)>
+
+7.2. TCPoXML DTD
+
+ <!--
+ DTD for TCP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ -->
+
+ <!-- the pseudoheader is only included for checksum calculations -->
+ <!ELEMENT tcp (tcp.pseudoheader?, tcp.header, payload)>
+
+ <!ELEMENT tcp.header (src, dest, sequence, acknowledgement, offset,
+ reserved, control, window, checksum, urgent,
+ tcp.options, padding)>
+
+ <!ELEMENT src EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+ <!ATTLIST src
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT dest EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+
+
+
+Kennedy Informational [Page 11]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST dest
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT sequence EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST sequence
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT acknowledgement EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST acknowledgement
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= number <= 255 -->
+ <!ATTLIST offset
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT reserved EMPTY>
+ <!ATTLIST reserved
+ value CDATA #FIXED "0">
+
+ <!ELEMENT control EMPTY>
+ <!ATTLIST control
+ urg (0|1) #IMPLIED
+ ack (0|1) #IMPLIED
+ psh (0|1) #IMPLIED
+ rst (0|1) #IMPLIED
+ syn (0|1) #IMPLIED
+ fin (0|1) #IMPLIED>
+
+ <!ELEMENT window EMPTY>
+ <!-- 0 <= size <= 65,535 -->
+ <!ATTLIST window
+ size %Digits; #REQUIRED>
+
+ <!--
+ checksum as in ip, but with
+ the following pseudo-header added into the tcp element:
+ -->
+ <!ELEMENT tcp.pseudoheader (source, destination, protocol,
+ tcp.length)>
+
+ <!--
+ tcp header + data length in octets. does not include the size of
+
+ the pseudoheader.
+ -->
+
+
+
+Kennedy Informational [Page 12]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ELEMENT tcp.length EMPTY>
+ <!ATTLIST tcp.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT urgent EMPTY>
+ <!-- 0 <= pointer <= 65,535 -->
+ <!ATTLIST urgent
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT tcp.options (tcp.end | tcp.noop | tcp.mss)+>
+
+ <!ELEMENT tcp.end EMPTY>
+ <!ATTLIST tcp.end
+ kind CDATA #FIXED "0">
+
+ <!ELEMENT tcp.noop EMPTY>
+ <!ATTLIST tcp.noop
+ kind CDATA #FIXED "1">
+
+ <!ELEMENT tcp.mss EMPTY>
+ <!ATTLIST tcp.mss
+ kind CDATA #FIXED "2"
+ length CDATA #FIXED "4"
+ size %Digits; #REQUIRED>
+
+7.3. UDPoXML DTD
+
+ <!--
+ DTD for UDP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ -->
+
+ <!ELEMENT udp (udp.pseudoheader?, udp.header, payload)>
+
+ <!ELEMENT udp.header (src, dest, udp.length, checksum)>
+
+ <!ELEMENT udp.pseudoheader (source, destination, protocol,
+ udp.length)>
+
+ <!--
+ udp header + data length in octets. does not include the size of
+ the pseudoheader.
+ -->
+ <!ELEMENT udp.length EMPTY>
+ <!ATTLIST udp.length
+ value %Digits; #REQUIRED>
+
+
+
+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:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ <ip>
+ <header length="474">
+ <version value="4"/>
+ <tos precedence="Routine" delay="Normal" throughput="Normal"
+ relibility="Normal" reserved="0"/>
+ <total.length value="461"/>
+ <id value="1"/>
+ <flags reserved="0" df="dont" mf="last"/>
+ <offset value="0"/>
+ <ttl value="255"/>
+ <protocol value="6"/>
+ <checksum value="8707"/>
+ <source address="10.0.0.22"/>
+ <destination address="10.0.0.1"/>
+ <options>
+ <end copied="0" class="0" number="0"/>
+ </options>
+ <padding pad="0"/>
+ </header>
+ <payload>
+ </payload>
+ </ip>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+3.2. Example Datagram
+
+ The following is an example TCPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ <tcp>
+ <tcp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <sequence number="322622954"/>
+ <acknowledgement number="689715995"/>
+ <offset number=""/>
+ <reserved value="0"/>
+ <control syn="1" ack="1"/>
+ <window size="1"/>
+ <urgent pointer="0"/>
+ <checksum value="2988"/>
+ <tcp.options>
+ <tcp.end kind="0"/>
+ </tcp.options>
+ <padding pad="0"/>
+ </tcp.header>
+ <payload>
+ </payload>
+ </tcp>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+4.2. Example Datagram
+
+ The following is an example UDPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ <udp>
+ <udp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <udp.length value="143"/>
+ <checksum value="2988"/>
+ </udp.header>
+ <payload>
+ </payload>
+ </udp>
+
+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 ("<?xml".)
+
+5.2. IEEE 802
+
+ BLOAT is encapsulated in IEEE 802 Networks as in [RFC1042] except
+ that the protocol type code for IPoXML is 0xBEEF.
+
+
+
+
+
+Kennedy Informational [Page 6]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+6. Gatewaying over IP
+
+ In order to facilitate the gradual introduction of BLOAT into the
+ public Internet, BLOAT MAY be encapsulated in IP as in [RFC2003] to
+ gateway between networks that run BLOAT natively on their LANs.
+
+7. DTDs
+
+ The Transport DTDs (7.2. and 7.3.) build on the definitions in the
+ Network DTD (7.1.)
+
+ The DTDs are referenced by their PubidLiteral and SystemLiteral (from
+ [XML]) although it is understood that most IPoXML implementations
+ will not need to pull down the DTD, as it will normally be embedded
+ in the implementation, and presents something of a catch-22 if you
+ need to load part of your network protocol over the network.
+
+7.1. IPoXML DTD
+
+ <!--
+ DTD for IP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ -->
+ <!--
+ DTD data types:
+
+ Digits [0..9]+
+
+ Precedence "NetworkControl | InternetworkControl |
+ CRITIC | FlashOverride | Flash | Immediate |
+ Priority | Routine"
+
+ IP4Addr "dotted-decimal" notation of [RFC1123]
+
+ Class [0..3]
+
+ Sec "Unclassified | Confidential | EFTO | MMMM | PROG |
+ Restricted | Secret | Top Secret | Reserved"
+
+ Compartments [0..65535]
+
+ Handling [0..65535]
+
+ TCC [0..16777216]
+
+ -->
+
+
+
+Kennedy Informational [Page 7]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ENTITY % Digits "CDATA">
+ <!ENTITY % Precedence "CDATA">
+ <!ENTITY % IP4Addr "CDATA">
+ <!ENTITY % Class "CDATA">
+ <!ENTITY % Sec "CDATA">
+ <!ENTITY % Compartments "CDATA">
+ <!ENTITY % Handling "CDATA">
+ <!ENTITY % TCC "CDATA">
+
+ <!ELEMENT ip (header, payload)>
+
+ <!ELEMENT header (version, tos, total.length, id, flags, offset, ttl,
+ protocol, checksum, source, destination, options,
+ padding)>
+ <!-- length of header in 32-bit words -->
+ <!ATTLIST header
+ length %Digits; #REQUIRED>
+
+ <!ELEMENT version EMPTY>
+ <!-- ip version. SHOULD be "4" -->
+ <!ATTLIST version
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT tos EMPTY>
+ <!ATTLIST tos
+ precedence %Precedence; #REQUIRED
+ delay (normal | low) #REQUIRED
+ throughput (normal | high) #REQUIRED
+ relibility (normal | high) #REQUIRED
+ reserved CDATA #FIXED "0">
+
+ <!ELEMENT total.length EMPTY>
+ <!--
+ total length of datagram (header and payload) in octets, MUST be
+ less than 65,535 (and SHOULD be less than 1024 for IPoXML on local
+ ethernets).
+ -->
+ <!ATTLIST total.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT id EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST id
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT flags EMPTY>
+ <!-- df = don't fragment, mf = more fragments -->
+ <!ATTLIST flags
+
+
+
+Kennedy Informational [Page 8]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ reserved CDATA #FIXED "0"
+ df (may|dont) #REQUIRED
+ mf (last|more) #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= offset <= 8192 measured in 8 octet (64-bit) chunks -->
+ <!ATTLIST offset
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT ttl EMPTY>
+ <!-- 0 <= ttl <= 255 -->
+ <!ATTLIST ttl
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT protocol EMPTY>
+ <!-- 0 <= protocol <= 255 (per IANA) -->
+ <!ATTLIST protocol
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT checksum EMPTY>
+ <!-- 0 <= checksum <= 65535 (over header only) -->
+ <!ATTLIST checksum
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT source EMPTY>
+ <!ATTLIST source
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT destination EMPTY>
+ <!ATTLIST destination
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT options ( end | noop | security | loose | strict | record
+ | stream | timestamp )*>
+
+ <!ELEMENT end EMPTY>
+ <!ATTLIST end
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "0">
+
+ <!ELEMENT noop EMPTY>
+ <!ATTLIST noop
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "1">
+
+ <!ELEMENT security EMPTY>
+
+
+
+Kennedy Informational [Page 9]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST security
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "2"
+ length CDATA #FIXED "11"
+ security %Sec; #REQUIRED
+ compartments %Compartments; #REQUIRED
+ handling %Handling; #REQUIRED
+ tcc %TCC; #REQUIRED>
+ <!ELEMENT loose (hop)+>
+ <!ATTLIST loose
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "3"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT hop EMPTY>
+ <!ATTLIST hop
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT strict (hop)+>
+ <!ATTLIST strict
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "9"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT record (hop)+>
+ <!ATTLIST record
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "7"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT stream EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST stream
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "8"
+ length CDATA #FIXED "4"
+ id %Digits; #REQUIRED>
+
+ <!ELEMENT timestamp (tstamp)+>
+ <!-- 0 <= oflw <=15 -->
+
+
+
+Kennedy Informational [Page 10]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST timestamp
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "2"
+ number CDATA #FIXED "4"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED
+ oflw %Digits; #REQUIRED
+ flag (0 | 1 | 3) #REQUIRED>
+
+ <!ELEMENT tstamp EMPTY>
+ <!ATTLIST tstamp
+ time %Digits; #REQUIRED
+ address %IP4Addr; #IMPLIED>
+ <!--
+ padding to bring header to 32-bit boundary.
+ pad MUST be "0"*
+ -->
+ <!ELEMENT padding EMPTY>
+ <!ATTLIST padding
+ pad CDATA #REQUIRED>
+
+ <!-- payload MUST be encoded as base-64 [RFC2045], as modified
+ by section 2.1 of this RFC -->
+ <!ELEMENT payload (CDATA)>
+
+7.2. TCPoXML DTD
+
+ <!--
+ DTD for TCP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ -->
+
+ <!-- the pseudoheader is only included for checksum calculations -->
+ <!ELEMENT tcp (tcp.pseudoheader?, tcp.header, payload)>
+
+ <!ELEMENT tcp.header (src, dest, sequence, acknowledgement, offset,
+ reserved, control, window, checksum, urgent,
+ tcp.options, padding)>
+
+ <!ELEMENT src EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+ <!ATTLIST src
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT dest EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+
+
+
+Kennedy Informational [Page 11]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST dest
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT sequence EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST sequence
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT acknowledgement EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST acknowledgement
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= number <= 255 -->
+ <!ATTLIST offset
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT reserved EMPTY>
+ <!ATTLIST reserved
+ value CDATA #FIXED "0">
+
+ <!ELEMENT control EMPTY>
+ <!ATTLIST control
+ urg (0|1) #IMPLIED
+ ack (0|1) #IMPLIED
+ psh (0|1) #IMPLIED
+ rst (0|1) #IMPLIED
+ syn (0|1) #IMPLIED
+ fin (0|1) #IMPLIED>
+
+ <!ELEMENT window EMPTY>
+ <!-- 0 <= size <= 65,535 -->
+ <!ATTLIST window
+ size %Digits; #REQUIRED>
+
+ <!--
+ checksum as in ip, but with
+ the following pseudo-header added into the tcp element:
+ -->
+ <!ELEMENT tcp.pseudoheader (source, destination, protocol,
+ tcp.length)>
+
+ <!--
+ tcp header + data length in octets. does not include the size of
+
+ the pseudoheader.
+ -->
+
+
+
+Kennedy Informational [Page 12]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ELEMENT tcp.length EMPTY>
+ <!ATTLIST tcp.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT urgent EMPTY>
+ <!-- 0 <= pointer <= 65,535 -->
+ <!ATTLIST urgent
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT tcp.options (tcp.end | tcp.noop | tcp.mss)+>
+
+ <!ELEMENT tcp.end EMPTY>
+ <!ATTLIST tcp.end
+ kind CDATA #FIXED "0">
+
+ <!ELEMENT tcp.noop EMPTY>
+ <!ATTLIST tcp.noop
+ kind CDATA #FIXED "1">
+
+ <!ELEMENT tcp.mss EMPTY>
+ <!ATTLIST tcp.mss
+ kind CDATA #FIXED "2"
+ length CDATA #FIXED "4"
+ size %Digits; #REQUIRED>
+
+7.3. UDPoXML DTD
+
+ <!--
+ DTD for UDP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ -->
+
+ <!ELEMENT udp (udp.pseudoheader?, udp.header, payload)>
+
+ <!ELEMENT udp.header (src, dest, udp.length, checksum)>
+
+ <!ELEMENT udp.pseudoheader (source, destination, protocol,
+ udp.length)>
+
+ <!--
+ udp header + data length in octets. does not include the size of
+ the pseudoheader.
+ -->
+ <!ELEMENT udp.length EMPTY>
+ <!ATTLIST udp.length
+ value %Digits; #REQUIRED>
+
+
+
+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:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ <ip>
+ <header length="474">
+ <version value="4"/>
+ <tos precedence="Routine" delay="Normal" throughput="Normal"
+ relibility="Normal" reserved="0"/>
+ <total.length value="461"/>
+ <id value="1"/>
+ <flags reserved="0" df="dont" mf="last"/>
+ <offset value="0"/>
+ <ttl value="255"/>
+ <protocol value="6"/>
+ <checksum value="8707"/>
+ <source address="10.0.0.22"/>
+ <destination address="10.0.0.1"/>
+ <options>
+ <end copied="0" class="0" number="0"/>
+ </options>
+ <padding pad="0"/>
+ </header>
+ <payload>
+ </payload>
+ </ip>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+3.2. Example Datagram
+
+ The following is an example TCPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ <tcp>
+ <tcp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <sequence number="322622954"/>
+ <acknowledgement number="689715995"/>
+ <offset number=""/>
+ <reserved value="0"/>
+ <control syn="1" ack="1"/>
+ <window size="1"/>
+ <urgent pointer="0"/>
+ <checksum value="2988"/>
+ <tcp.options>
+ <tcp.end kind="0"/>
+ </tcp.options>
+ <padding pad="0"/>
+ </tcp.header>
+ <payload>
+ </payload>
+ </tcp>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+4.2. Example Datagram
+
+ The following is an example UDPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ <udp>
+ <udp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <udp.length value="143"/>
+ <checksum value="2988"/>
+ </udp.header>
+ <payload>
+ </payload>
+ </udp>
+
+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 ("<?xml".)
+
+5.2. IEEE 802
+
+ BLOAT is encapsulated in IEEE 802 Networks as in [RFC1042] except
+ that the protocol type code for IPoXML is 0xBEEF.
+
+
+
+
+
+Kennedy Informational [Page 6]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+6. Gatewaying over IP
+
+ In order to facilitate the gradual introduction of BLOAT into the
+ public Internet, BLOAT MAY be encapsulated in IP as in [RFC2003] to
+ gateway between networks that run BLOAT natively on their LANs.
+
+7. DTDs
+
+ The Transport DTDs (7.2. and 7.3.) build on the definitions in the
+ Network DTD (7.1.)
+
+ The DTDs are referenced by their PubidLiteral and SystemLiteral (from
+ [XML]) although it is understood that most IPoXML implementations
+ will not need to pull down the DTD, as it will normally be embedded
+ in the implementation, and presents something of a catch-22 if you
+ need to load part of your network protocol over the network.
+
+7.1. IPoXML DTD
+
+ <!--
+ DTD for IP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ -->
+ <!--
+ DTD data types:
+
+ Digits [0..9]+
+
+ Precedence "NetworkControl | InternetworkControl |
+ CRITIC | FlashOverride | Flash | Immediate |
+ Priority | Routine"
+
+ IP4Addr "dotted-decimal" notation of [RFC1123]
+
+ Class [0..3]
+
+ Sec "Unclassified | Confidential | EFTO | MMMM | PROG |
+ Restricted | Secret | Top Secret | Reserved"
+
+ Compartments [0..65535]
+
+ Handling [0..65535]
+
+ TCC [0..16777216]
+
+ -->
+
+
+
+Kennedy Informational [Page 7]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ENTITY % Digits "CDATA">
+ <!ENTITY % Precedence "CDATA">
+ <!ENTITY % IP4Addr "CDATA">
+ <!ENTITY % Class "CDATA">
+ <!ENTITY % Sec "CDATA">
+ <!ENTITY % Compartments "CDATA">
+ <!ENTITY % Handling "CDATA">
+ <!ENTITY % TCC "CDATA">
+
+ <!ELEMENT ip (header, payload)>
+
+ <!ELEMENT header (version, tos, total.length, id, flags, offset, ttl,
+ protocol, checksum, source, destination, options,
+ padding)>
+ <!-- length of header in 32-bit words -->
+ <!ATTLIST header
+ length %Digits; #REQUIRED>
+
+ <!ELEMENT version EMPTY>
+ <!-- ip version. SHOULD be "4" -->
+ <!ATTLIST version
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT tos EMPTY>
+ <!ATTLIST tos
+ precedence %Precedence; #REQUIRED
+ delay (normal | low) #REQUIRED
+ throughput (normal | high) #REQUIRED
+ relibility (normal | high) #REQUIRED
+ reserved CDATA #FIXED "0">
+
+ <!ELEMENT total.length EMPTY>
+ <!--
+ total length of datagram (header and payload) in octets, MUST be
+ less than 65,535 (and SHOULD be less than 1024 for IPoXML on local
+ ethernets).
+ -->
+ <!ATTLIST total.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT id EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST id
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT flags EMPTY>
+ <!-- df = don't fragment, mf = more fragments -->
+ <!ATTLIST flags
+
+
+
+Kennedy Informational [Page 8]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ reserved CDATA #FIXED "0"
+ df (may|dont) #REQUIRED
+ mf (last|more) #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= offset <= 8192 measured in 8 octet (64-bit) chunks -->
+ <!ATTLIST offset
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT ttl EMPTY>
+ <!-- 0 <= ttl <= 255 -->
+ <!ATTLIST ttl
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT protocol EMPTY>
+ <!-- 0 <= protocol <= 255 (per IANA) -->
+ <!ATTLIST protocol
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT checksum EMPTY>
+ <!-- 0 <= checksum <= 65535 (over header only) -->
+ <!ATTLIST checksum
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT source EMPTY>
+ <!ATTLIST source
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT destination EMPTY>
+ <!ATTLIST destination
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT options ( end | noop | security | loose | strict | record
+ | stream | timestamp )*>
+
+ <!ELEMENT end EMPTY>
+ <!ATTLIST end
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "0">
+
+ <!ELEMENT noop EMPTY>
+ <!ATTLIST noop
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "1">
+
+ <!ELEMENT security EMPTY>
+
+
+
+Kennedy Informational [Page 9]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST security
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "2"
+ length CDATA #FIXED "11"
+ security %Sec; #REQUIRED
+ compartments %Compartments; #REQUIRED
+ handling %Handling; #REQUIRED
+ tcc %TCC; #REQUIRED>
+ <!ELEMENT loose (hop)+>
+ <!ATTLIST loose
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "3"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT hop EMPTY>
+ <!ATTLIST hop
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT strict (hop)+>
+ <!ATTLIST strict
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "9"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT record (hop)+>
+ <!ATTLIST record
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "7"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT stream EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST stream
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "8"
+ length CDATA #FIXED "4"
+ id %Digits; #REQUIRED>
+
+ <!ELEMENT timestamp (tstamp)+>
+ <!-- 0 <= oflw <=15 -->
+
+
+
+Kennedy Informational [Page 10]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST timestamp
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "2"
+ number CDATA #FIXED "4"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED
+ oflw %Digits; #REQUIRED
+ flag (0 | 1 | 3) #REQUIRED>
+
+ <!ELEMENT tstamp EMPTY>
+ <!ATTLIST tstamp
+ time %Digits; #REQUIRED
+ address %IP4Addr; #IMPLIED>
+ <!--
+ padding to bring header to 32-bit boundary.
+ pad MUST be "0"*
+ -->
+ <!ELEMENT padding EMPTY>
+ <!ATTLIST padding
+ pad CDATA #REQUIRED>
+
+ <!-- payload MUST be encoded as base-64 [RFC2045], as modified
+ by section 2.1 of this RFC -->
+ <!ELEMENT payload (CDATA)>
+
+7.2. TCPoXML DTD
+
+ <!--
+ DTD for TCP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ -->
+
+ <!-- the pseudoheader is only included for checksum calculations -->
+ <!ELEMENT tcp (tcp.pseudoheader?, tcp.header, payload)>
+
+ <!ELEMENT tcp.header (src, dest, sequence, acknowledgement, offset,
+ reserved, control, window, checksum, urgent,
+ tcp.options, padding)>
+
+ <!ELEMENT src EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+ <!ATTLIST src
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT dest EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+
+
+
+Kennedy Informational [Page 11]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST dest
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT sequence EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST sequence
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT acknowledgement EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST acknowledgement
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= number <= 255 -->
+ <!ATTLIST offset
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT reserved EMPTY>
+ <!ATTLIST reserved
+ value CDATA #FIXED "0">
+
+ <!ELEMENT control EMPTY>
+ <!ATTLIST control
+ urg (0|1) #IMPLIED
+ ack (0|1) #IMPLIED
+ psh (0|1) #IMPLIED
+ rst (0|1) #IMPLIED
+ syn (0|1) #IMPLIED
+ fin (0|1) #IMPLIED>
+
+ <!ELEMENT window EMPTY>
+ <!-- 0 <= size <= 65,535 -->
+ <!ATTLIST window
+ size %Digits; #REQUIRED>
+
+ <!--
+ checksum as in ip, but with
+ the following pseudo-header added into the tcp element:
+ -->
+ <!ELEMENT tcp.pseudoheader (source, destination, protocol,
+ tcp.length)>
+
+ <!--
+ tcp header + data length in octets. does not include the size of
+
+ the pseudoheader.
+ -->
+
+
+
+Kennedy Informational [Page 12]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ELEMENT tcp.length EMPTY>
+ <!ATTLIST tcp.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT urgent EMPTY>
+ <!-- 0 <= pointer <= 65,535 -->
+ <!ATTLIST urgent
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT tcp.options (tcp.end | tcp.noop | tcp.mss)+>
+
+ <!ELEMENT tcp.end EMPTY>
+ <!ATTLIST tcp.end
+ kind CDATA #FIXED "0">
+
+ <!ELEMENT tcp.noop EMPTY>
+ <!ATTLIST tcp.noop
+ kind CDATA #FIXED "1">
+
+ <!ELEMENT tcp.mss EMPTY>
+ <!ATTLIST tcp.mss
+ kind CDATA #FIXED "2"
+ length CDATA #FIXED "4"
+ size %Digits; #REQUIRED>
+
+7.3. UDPoXML DTD
+
+ <!--
+ DTD for UDP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ -->
+
+ <!ELEMENT udp (udp.pseudoheader?, udp.header, payload)>
+
+ <!ELEMENT udp.header (src, dest, udp.length, checksum)>
+
+ <!ELEMENT udp.pseudoheader (source, destination, protocol,
+ udp.length)>
+
+ <!--
+ udp header + data length in octets. does not include the size of
+ the pseudoheader.
+ -->
+ <!ELEMENT udp.length EMPTY>
+ <!ATTLIST udp.length
+ value %Digits; #REQUIRED>
+
+
+
+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:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ <ip>
+ <header length="474">
+ <version value="4"/>
+ <tos precedence="Routine" delay="Normal" throughput="Normal"
+ relibility="Normal" reserved="0"/>
+ <total.length value="461"/>
+ <id value="1"/>
+ <flags reserved="0" df="dont" mf="last"/>
+ <offset value="0"/>
+ <ttl value="255"/>
+ <protocol value="6"/>
+ <checksum value="8707"/>
+ <source address="10.0.0.22"/>
+ <destination address="10.0.0.1"/>
+ <options>
+ <end copied="0" class="0" number="0"/>
+ </options>
+ <padding pad="0"/>
+ </header>
+ <payload>
+ </payload>
+ </ip>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+3.2. Example Datagram
+
+ The following is an example TCPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ <tcp>
+ <tcp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <sequence number="322622954"/>
+ <acknowledgement number="689715995"/>
+ <offset number=""/>
+ <reserved value="0"/>
+ <control syn="1" ack="1"/>
+ <window size="1"/>
+ <urgent pointer="0"/>
+ <checksum value="2988"/>
+ <tcp.options>
+ <tcp.end kind="0"/>
+ </tcp.options>
+ <padding pad="0"/>
+ </tcp.header>
+ <payload>
+ </payload>
+ </tcp>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+4.2. Example Datagram
+
+ The following is an example UDPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ <udp>
+ <udp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <udp.length value="143"/>
+ <checksum value="2988"/>
+ </udp.header>
+ <payload>
+ </payload>
+ </udp>
+
+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 ("<?xml".)
+
+5.2. IEEE 802
+
+ BLOAT is encapsulated in IEEE 802 Networks as in [RFC1042] except
+ that the protocol type code for IPoXML is 0xBEEF.
+
+
+
+
+
+Kennedy Informational [Page 6]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+6. Gatewaying over IP
+
+ In order to facilitate the gradual introduction of BLOAT into the
+ public Internet, BLOAT MAY be encapsulated in IP as in [RFC2003] to
+ gateway between networks that run BLOAT natively on their LANs.
+
+7. DTDs
+
+ The Transport DTDs (7.2. and 7.3.) build on the definitions in the
+ Network DTD (7.1.)
+
+ The DTDs are referenced by their PubidLiteral and SystemLiteral (from
+ [XML]) although it is understood that most IPoXML implementations
+ will not need to pull down the DTD, as it will normally be embedded
+ in the implementation, and presents something of a catch-22 if you
+ need to load part of your network protocol over the network.
+
+7.1. IPoXML DTD
+
+ <!--
+ DTD for IP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ -->
+ <!--
+ DTD data types:
+
+ Digits [0..9]+
+
+ Precedence "NetworkControl | InternetworkControl |
+ CRITIC | FlashOverride | Flash | Immediate |
+ Priority | Routine"
+
+ IP4Addr "dotted-decimal" notation of [RFC1123]
+
+ Class [0..3]
+
+ Sec "Unclassified | Confidential | EFTO | MMMM | PROG |
+ Restricted | Secret | Top Secret | Reserved"
+
+ Compartments [0..65535]
+
+ Handling [0..65535]
+
+ TCC [0..16777216]
+
+ -->
+
+
+
+Kennedy Informational [Page 7]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ENTITY % Digits "CDATA">
+ <!ENTITY % Precedence "CDATA">
+ <!ENTITY % IP4Addr "CDATA">
+ <!ENTITY % Class "CDATA">
+ <!ENTITY % Sec "CDATA">
+ <!ENTITY % Compartments "CDATA">
+ <!ENTITY % Handling "CDATA">
+ <!ENTITY % TCC "CDATA">
+
+ <!ELEMENT ip (header, payload)>
+
+ <!ELEMENT header (version, tos, total.length, id, flags, offset, ttl,
+ protocol, checksum, source, destination, options,
+ padding)>
+ <!-- length of header in 32-bit words -->
+ <!ATTLIST header
+ length %Digits; #REQUIRED>
+
+ <!ELEMENT version EMPTY>
+ <!-- ip version. SHOULD be "4" -->
+ <!ATTLIST version
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT tos EMPTY>
+ <!ATTLIST tos
+ precedence %Precedence; #REQUIRED
+ delay (normal | low) #REQUIRED
+ throughput (normal | high) #REQUIRED
+ relibility (normal | high) #REQUIRED
+ reserved CDATA #FIXED "0">
+
+ <!ELEMENT total.length EMPTY>
+ <!--
+ total length of datagram (header and payload) in octets, MUST be
+ less than 65,535 (and SHOULD be less than 1024 for IPoXML on local
+ ethernets).
+ -->
+ <!ATTLIST total.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT id EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST id
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT flags EMPTY>
+ <!-- df = don't fragment, mf = more fragments -->
+ <!ATTLIST flags
+
+
+
+Kennedy Informational [Page 8]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ reserved CDATA #FIXED "0"
+ df (may|dont) #REQUIRED
+ mf (last|more) #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= offset <= 8192 measured in 8 octet (64-bit) chunks -->
+ <!ATTLIST offset
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT ttl EMPTY>
+ <!-- 0 <= ttl <= 255 -->
+ <!ATTLIST ttl
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT protocol EMPTY>
+ <!-- 0 <= protocol <= 255 (per IANA) -->
+ <!ATTLIST protocol
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT checksum EMPTY>
+ <!-- 0 <= checksum <= 65535 (over header only) -->
+ <!ATTLIST checksum
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT source EMPTY>
+ <!ATTLIST source
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT destination EMPTY>
+ <!ATTLIST destination
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT options ( end | noop | security | loose | strict | record
+ | stream | timestamp )*>
+
+ <!ELEMENT end EMPTY>
+ <!ATTLIST end
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "0">
+
+ <!ELEMENT noop EMPTY>
+ <!ATTLIST noop
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "1">
+
+ <!ELEMENT security EMPTY>
+
+
+
+Kennedy Informational [Page 9]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST security
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "2"
+ length CDATA #FIXED "11"
+ security %Sec; #REQUIRED
+ compartments %Compartments; #REQUIRED
+ handling %Handling; #REQUIRED
+ tcc %TCC; #REQUIRED>
+ <!ELEMENT loose (hop)+>
+ <!ATTLIST loose
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "3"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT hop EMPTY>
+ <!ATTLIST hop
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT strict (hop)+>
+ <!ATTLIST strict
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "9"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT record (hop)+>
+ <!ATTLIST record
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "7"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT stream EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST stream
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "8"
+ length CDATA #FIXED "4"
+ id %Digits; #REQUIRED>
+
+ <!ELEMENT timestamp (tstamp)+>
+ <!-- 0 <= oflw <=15 -->
+
+
+
+Kennedy Informational [Page 10]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST timestamp
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "2"
+ number CDATA #FIXED "4"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED
+ oflw %Digits; #REQUIRED
+ flag (0 | 1 | 3) #REQUIRED>
+
+ <!ELEMENT tstamp EMPTY>
+ <!ATTLIST tstamp
+ time %Digits; #REQUIRED
+ address %IP4Addr; #IMPLIED>
+ <!--
+ padding to bring header to 32-bit boundary.
+ pad MUST be "0"*
+ -->
+ <!ELEMENT padding EMPTY>
+ <!ATTLIST padding
+ pad CDATA #REQUIRED>
+
+ <!-- payload MUST be encoded as base-64 [RFC2045], as modified
+ by section 2.1 of this RFC -->
+ <!ELEMENT payload (CDATA)>
+
+7.2. TCPoXML DTD
+
+ <!--
+ DTD for TCP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ -->
+
+ <!-- the pseudoheader is only included for checksum calculations -->
+ <!ELEMENT tcp (tcp.pseudoheader?, tcp.header, payload)>
+
+ <!ELEMENT tcp.header (src, dest, sequence, acknowledgement, offset,
+ reserved, control, window, checksum, urgent,
+ tcp.options, padding)>
+
+ <!ELEMENT src EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+ <!ATTLIST src
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT dest EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+
+
+
+Kennedy Informational [Page 11]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST dest
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT sequence EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST sequence
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT acknowledgement EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST acknowledgement
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= number <= 255 -->
+ <!ATTLIST offset
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT reserved EMPTY>
+ <!ATTLIST reserved
+ value CDATA #FIXED "0">
+
+ <!ELEMENT control EMPTY>
+ <!ATTLIST control
+ urg (0|1) #IMPLIED
+ ack (0|1) #IMPLIED
+ psh (0|1) #IMPLIED
+ rst (0|1) #IMPLIED
+ syn (0|1) #IMPLIED
+ fin (0|1) #IMPLIED>
+
+ <!ELEMENT window EMPTY>
+ <!-- 0 <= size <= 65,535 -->
+ <!ATTLIST window
+ size %Digits; #REQUIRED>
+
+ <!--
+ checksum as in ip, but with
+ the following pseudo-header added into the tcp element:
+ -->
+ <!ELEMENT tcp.pseudoheader (source, destination, protocol,
+ tcp.length)>
+
+ <!--
+ tcp header + data length in octets. does not include the size of
+
+ the pseudoheader.
+ -->
+
+
+
+Kennedy Informational [Page 12]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ELEMENT tcp.length EMPTY>
+ <!ATTLIST tcp.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT urgent EMPTY>
+ <!-- 0 <= pointer <= 65,535 -->
+ <!ATTLIST urgent
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT tcp.options (tcp.end | tcp.noop | tcp.mss)+>
+
+ <!ELEMENT tcp.end EMPTY>
+ <!ATTLIST tcp.end
+ kind CDATA #FIXED "0">
+
+ <!ELEMENT tcp.noop EMPTY>
+ <!ATTLIST tcp.noop
+ kind CDATA #FIXED "1">
+
+ <!ELEMENT tcp.mss EMPTY>
+ <!ATTLIST tcp.mss
+ kind CDATA #FIXED "2"
+ length CDATA #FIXED "4"
+ size %Digits; #REQUIRED>
+
+7.3. UDPoXML DTD
+
+ <!--
+ DTD for UDP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ -->
+
+ <!ELEMENT udp (udp.pseudoheader?, udp.header, payload)>
+
+ <!ELEMENT udp.header (src, dest, udp.length, checksum)>
+
+ <!ELEMENT udp.pseudoheader (source, destination, protocol,
+ udp.length)>
+
+ <!--
+ udp header + data length in octets. does not include the size of
+ the pseudoheader.
+ -->
+ <!ELEMENT udp.length EMPTY>
+ <!ATTLIST udp.length
+ value %Digits; #REQUIRED>
+
+
+
+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:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ <ip>
+ <header length="474">
+ <version value="4"/>
+ <tos precedence="Routine" delay="Normal" throughput="Normal"
+ relibility="Normal" reserved="0"/>
+ <total.length value="461"/>
+ <id value="1"/>
+ <flags reserved="0" df="dont" mf="last"/>
+ <offset value="0"/>
+ <ttl value="255"/>
+ <protocol value="6"/>
+ <checksum value="8707"/>
+ <source address="10.0.0.22"/>
+ <destination address="10.0.0.1"/>
+ <options>
+ <end copied="0" class="0" number="0"/>
+ </options>
+ <padding pad="0"/>
+ </header>
+ <payload>
+ </payload>
+ </ip>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+3.2. Example Datagram
+
+ The following is an example TCPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ <tcp>
+ <tcp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <sequence number="322622954"/>
+ <acknowledgement number="689715995"/>
+ <offset number=""/>
+ <reserved value="0"/>
+ <control syn="1" ack="1"/>
+ <window size="1"/>
+ <urgent pointer="0"/>
+ <checksum value="2988"/>
+ <tcp.options>
+ <tcp.end kind="0"/>
+ </tcp.options>
+ <padding pad="0"/>
+ </tcp.header>
+ <payload>
+ </payload>
+ </tcp>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+4.2. Example Datagram
+
+ The following is an example UDPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ <udp>
+ <udp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <udp.length value="143"/>
+ <checksum value="2988"/>
+ </udp.header>
+ <payload>
+ </payload>
+ </udp>
+
+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 ("<?xml".)
+
+5.2. IEEE 802
+
+ BLOAT is encapsulated in IEEE 802 Networks as in [RFC1042] except
+ that the protocol type code for IPoXML is 0xBEEF.
+
+
+
+
+
+Kennedy Informational [Page 6]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+6. Gatewaying over IP
+
+ In order to facilitate the gradual introduction of BLOAT into the
+ public Internet, BLOAT MAY be encapsulated in IP as in [RFC2003] to
+ gateway between networks that run BLOAT natively on their LANs.
+
+7. DTDs
+
+ The Transport DTDs (7.2. and 7.3.) build on the definitions in the
+ Network DTD (7.1.)
+
+ The DTDs are referenced by their PubidLiteral and SystemLiteral (from
+ [XML]) although it is understood that most IPoXML implementations
+ will not need to pull down the DTD, as it will normally be embedded
+ in the implementation, and presents something of a catch-22 if you
+ need to load part of your network protocol over the network.
+
+7.1. IPoXML DTD
+
+ <!--
+ DTD for IP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ -->
+ <!--
+ DTD data types:
+
+ Digits [0..9]+
+
+ Precedence "NetworkControl | InternetworkControl |
+ CRITIC | FlashOverride | Flash | Immediate |
+ Priority | Routine"
+
+ IP4Addr "dotted-decimal" notation of [RFC1123]
+
+ Class [0..3]
+
+ Sec "Unclassified | Confidential | EFTO | MMMM | PROG |
+ Restricted | Secret | Top Secret | Reserved"
+
+ Compartments [0..65535]
+
+ Handling [0..65535]
+
+ TCC [0..16777216]
+
+ -->
+
+
+
+Kennedy Informational [Page 7]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ENTITY % Digits "CDATA">
+ <!ENTITY % Precedence "CDATA">
+ <!ENTITY % IP4Addr "CDATA">
+ <!ENTITY % Class "CDATA">
+ <!ENTITY % Sec "CDATA">
+ <!ENTITY % Compartments "CDATA">
+ <!ENTITY % Handling "CDATA">
+ <!ENTITY % TCC "CDATA">
+
+ <!ELEMENT ip (header, payload)>
+
+ <!ELEMENT header (version, tos, total.length, id, flags, offset, ttl,
+ protocol, checksum, source, destination, options,
+ padding)>
+ <!-- length of header in 32-bit words -->
+ <!ATTLIST header
+ length %Digits; #REQUIRED>
+
+ <!ELEMENT version EMPTY>
+ <!-- ip version. SHOULD be "4" -->
+ <!ATTLIST version
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT tos EMPTY>
+ <!ATTLIST tos
+ precedence %Precedence; #REQUIRED
+ delay (normal | low) #REQUIRED
+ throughput (normal | high) #REQUIRED
+ relibility (normal | high) #REQUIRED
+ reserved CDATA #FIXED "0">
+
+ <!ELEMENT total.length EMPTY>
+ <!--
+ total length of datagram (header and payload) in octets, MUST be
+ less than 65,535 (and SHOULD be less than 1024 for IPoXML on local
+ ethernets).
+ -->
+ <!ATTLIST total.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT id EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST id
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT flags EMPTY>
+ <!-- df = don't fragment, mf = more fragments -->
+ <!ATTLIST flags
+
+
+
+Kennedy Informational [Page 8]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ reserved CDATA #FIXED "0"
+ df (may|dont) #REQUIRED
+ mf (last|more) #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= offset <= 8192 measured in 8 octet (64-bit) chunks -->
+ <!ATTLIST offset
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT ttl EMPTY>
+ <!-- 0 <= ttl <= 255 -->
+ <!ATTLIST ttl
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT protocol EMPTY>
+ <!-- 0 <= protocol <= 255 (per IANA) -->
+ <!ATTLIST protocol
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT checksum EMPTY>
+ <!-- 0 <= checksum <= 65535 (over header only) -->
+ <!ATTLIST checksum
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT source EMPTY>
+ <!ATTLIST source
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT destination EMPTY>
+ <!ATTLIST destination
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT options ( end | noop | security | loose | strict | record
+ | stream | timestamp )*>
+
+ <!ELEMENT end EMPTY>
+ <!ATTLIST end
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "0">
+
+ <!ELEMENT noop EMPTY>
+ <!ATTLIST noop
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "1">
+
+ <!ELEMENT security EMPTY>
+
+
+
+Kennedy Informational [Page 9]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST security
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "2"
+ length CDATA #FIXED "11"
+ security %Sec; #REQUIRED
+ compartments %Compartments; #REQUIRED
+ handling %Handling; #REQUIRED
+ tcc %TCC; #REQUIRED>
+ <!ELEMENT loose (hop)+>
+ <!ATTLIST loose
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "3"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT hop EMPTY>
+ <!ATTLIST hop
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT strict (hop)+>
+ <!ATTLIST strict
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "9"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT record (hop)+>
+ <!ATTLIST record
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "7"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT stream EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST stream
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "8"
+ length CDATA #FIXED "4"
+ id %Digits; #REQUIRED>
+
+ <!ELEMENT timestamp (tstamp)+>
+ <!-- 0 <= oflw <=15 -->
+
+
+
+Kennedy Informational [Page 10]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST timestamp
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "2"
+ number CDATA #FIXED "4"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED
+ oflw %Digits; #REQUIRED
+ flag (0 | 1 | 3) #REQUIRED>
+
+ <!ELEMENT tstamp EMPTY>
+ <!ATTLIST tstamp
+ time %Digits; #REQUIRED
+ address %IP4Addr; #IMPLIED>
+ <!--
+ padding to bring header to 32-bit boundary.
+ pad MUST be "0"*
+ -->
+ <!ELEMENT padding EMPTY>
+ <!ATTLIST padding
+ pad CDATA #REQUIRED>
+
+ <!-- payload MUST be encoded as base-64 [RFC2045], as modified
+ by section 2.1 of this RFC -->
+ <!ELEMENT payload (CDATA)>
+
+7.2. TCPoXML DTD
+
+ <!--
+ DTD for TCP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ -->
+
+ <!-- the pseudoheader is only included for checksum calculations -->
+ <!ELEMENT tcp (tcp.pseudoheader?, tcp.header, payload)>
+
+ <!ELEMENT tcp.header (src, dest, sequence, acknowledgement, offset,
+ reserved, control, window, checksum, urgent,
+ tcp.options, padding)>
+
+ <!ELEMENT src EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+ <!ATTLIST src
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT dest EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+
+
+
+Kennedy Informational [Page 11]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST dest
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT sequence EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST sequence
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT acknowledgement EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST acknowledgement
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= number <= 255 -->
+ <!ATTLIST offset
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT reserved EMPTY>
+ <!ATTLIST reserved
+ value CDATA #FIXED "0">
+
+ <!ELEMENT control EMPTY>
+ <!ATTLIST control
+ urg (0|1) #IMPLIED
+ ack (0|1) #IMPLIED
+ psh (0|1) #IMPLIED
+ rst (0|1) #IMPLIED
+ syn (0|1) #IMPLIED
+ fin (0|1) #IMPLIED>
+
+ <!ELEMENT window EMPTY>
+ <!-- 0 <= size <= 65,535 -->
+ <!ATTLIST window
+ size %Digits; #REQUIRED>
+
+ <!--
+ checksum as in ip, but with
+ the following pseudo-header added into the tcp element:
+ -->
+ <!ELEMENT tcp.pseudoheader (source, destination, protocol,
+ tcp.length)>
+
+ <!--
+ tcp header + data length in octets. does not include the size of
+
+ the pseudoheader.
+ -->
+
+
+
+Kennedy Informational [Page 12]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ELEMENT tcp.length EMPTY>
+ <!ATTLIST tcp.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT urgent EMPTY>
+ <!-- 0 <= pointer <= 65,535 -->
+ <!ATTLIST urgent
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT tcp.options (tcp.end | tcp.noop | tcp.mss)+>
+
+ <!ELEMENT tcp.end EMPTY>
+ <!ATTLIST tcp.end
+ kind CDATA #FIXED "0">
+
+ <!ELEMENT tcp.noop EMPTY>
+ <!ATTLIST tcp.noop
+ kind CDATA #FIXED "1">
+
+ <!ELEMENT tcp.mss EMPTY>
+ <!ATTLIST tcp.mss
+ kind CDATA #FIXED "2"
+ length CDATA #FIXED "4"
+ size %Digits; #REQUIRED>
+
+7.3. UDPoXML DTD
+
+ <!--
+ DTD for UDP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ -->
+
+ <!ELEMENT udp (udp.pseudoheader?, udp.header, payload)>
+
+ <!ELEMENT udp.header (src, dest, udp.length, checksum)>
+
+ <!ELEMENT udp.pseudoheader (source, destination, protocol,
+ udp.length)>
+
+ <!--
+ udp header + data length in octets. does not include the size of
+ the pseudoheader.
+ -->
+ <!ELEMENT udp.length EMPTY>
+ <!ATTLIST udp.length
+ value %Digits; #REQUIRED>
+
+
+
+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:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ <ip>
+ <header length="474">
+ <version value="4"/>
+ <tos precedence="Routine" delay="Normal" throughput="Normal"
+ relibility="Normal" reserved="0"/>
+ <total.length value="461"/>
+ <id value="1"/>
+ <flags reserved="0" df="dont" mf="last"/>
+ <offset value="0"/>
+ <ttl value="255"/>
+ <protocol value="6"/>
+ <checksum value="8707"/>
+ <source address="10.0.0.22"/>
+ <destination address="10.0.0.1"/>
+ <options>
+ <end copied="0" class="0" number="0"/>
+ </options>
+ <padding pad="0"/>
+ </header>
+ <payload>
+ </payload>
+ </ip>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+3.2. Example Datagram
+
+ The following is an example TCPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ <tcp>
+ <tcp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <sequence number="322622954"/>
+ <acknowledgement number="689715995"/>
+ <offset number=""/>
+ <reserved value="0"/>
+ <control syn="1" ack="1"/>
+ <window size="1"/>
+ <urgent pointer="0"/>
+ <checksum value="2988"/>
+ <tcp.options>
+ <tcp.end kind="0"/>
+ </tcp.options>
+ <padding pad="0"/>
+ </tcp.header>
+ <payload>
+ </payload>
+ </tcp>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+4.2. Example Datagram
+
+ The following is an example UDPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ <udp>
+ <udp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <udp.length value="143"/>
+ <checksum value="2988"/>
+ </udp.header>
+ <payload>
+ </payload>
+ </udp>
+
+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 ("<?xml".)
+
+5.2. IEEE 802
+
+ BLOAT is encapsulated in IEEE 802 Networks as in [RFC1042] except
+ that the protocol type code for IPoXML is 0xBEEF.
+
+
+
+
+
+Kennedy Informational [Page 6]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+6. Gatewaying over IP
+
+ In order to facilitate the gradual introduction of BLOAT into the
+ public Internet, BLOAT MAY be encapsulated in IP as in [RFC2003] to
+ gateway between networks that run BLOAT natively on their LANs.
+
+7. DTDs
+
+ The Transport DTDs (7.2. and 7.3.) build on the definitions in the
+ Network DTD (7.1.)
+
+ The DTDs are referenced by their PubidLiteral and SystemLiteral (from
+ [XML]) although it is understood that most IPoXML implementations
+ will not need to pull down the DTD, as it will normally be embedded
+ in the implementation, and presents something of a catch-22 if you
+ need to load part of your network protocol over the network.
+
+7.1. IPoXML DTD
+
+ <!--
+ DTD for IP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ -->
+ <!--
+ DTD data types:
+
+ Digits [0..9]+
+
+ Precedence "NetworkControl | InternetworkControl |
+ CRITIC | FlashOverride | Flash | Immediate |
+ Priority | Routine"
+
+ IP4Addr "dotted-decimal" notation of [RFC1123]
+
+ Class [0..3]
+
+ Sec "Unclassified | Confidential | EFTO | MMMM | PROG |
+ Restricted | Secret | Top Secret | Reserved"
+
+ Compartments [0..65535]
+
+ Handling [0..65535]
+
+ TCC [0..16777216]
+
+ -->
+
+
+
+Kennedy Informational [Page 7]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ENTITY % Digits "CDATA">
+ <!ENTITY % Precedence "CDATA">
+ <!ENTITY % IP4Addr "CDATA">
+ <!ENTITY % Class "CDATA">
+ <!ENTITY % Sec "CDATA">
+ <!ENTITY % Compartments "CDATA">
+ <!ENTITY % Handling "CDATA">
+ <!ENTITY % TCC "CDATA">
+
+ <!ELEMENT ip (header, payload)>
+
+ <!ELEMENT header (version, tos, total.length, id, flags, offset, ttl,
+ protocol, checksum, source, destination, options,
+ padding)>
+ <!-- length of header in 32-bit words -->
+ <!ATTLIST header
+ length %Digits; #REQUIRED>
+
+ <!ELEMENT version EMPTY>
+ <!-- ip version. SHOULD be "4" -->
+ <!ATTLIST version
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT tos EMPTY>
+ <!ATTLIST tos
+ precedence %Precedence; #REQUIRED
+ delay (normal | low) #REQUIRED
+ throughput (normal | high) #REQUIRED
+ relibility (normal | high) #REQUIRED
+ reserved CDATA #FIXED "0">
+
+ <!ELEMENT total.length EMPTY>
+ <!--
+ total length of datagram (header and payload) in octets, MUST be
+ less than 65,535 (and SHOULD be less than 1024 for IPoXML on local
+ ethernets).
+ -->
+ <!ATTLIST total.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT id EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST id
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT flags EMPTY>
+ <!-- df = don't fragment, mf = more fragments -->
+ <!ATTLIST flags
+
+
+
+Kennedy Informational [Page 8]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ reserved CDATA #FIXED "0"
+ df (may|dont) #REQUIRED
+ mf (last|more) #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= offset <= 8192 measured in 8 octet (64-bit) chunks -->
+ <!ATTLIST offset
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT ttl EMPTY>
+ <!-- 0 <= ttl <= 255 -->
+ <!ATTLIST ttl
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT protocol EMPTY>
+ <!-- 0 <= protocol <= 255 (per IANA) -->
+ <!ATTLIST protocol
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT checksum EMPTY>
+ <!-- 0 <= checksum <= 65535 (over header only) -->
+ <!ATTLIST checksum
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT source EMPTY>
+ <!ATTLIST source
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT destination EMPTY>
+ <!ATTLIST destination
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT options ( end | noop | security | loose | strict | record
+ | stream | timestamp )*>
+
+ <!ELEMENT end EMPTY>
+ <!ATTLIST end
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "0">
+
+ <!ELEMENT noop EMPTY>
+ <!ATTLIST noop
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "1">
+
+ <!ELEMENT security EMPTY>
+
+
+
+Kennedy Informational [Page 9]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST security
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "2"
+ length CDATA #FIXED "11"
+ security %Sec; #REQUIRED
+ compartments %Compartments; #REQUIRED
+ handling %Handling; #REQUIRED
+ tcc %TCC; #REQUIRED>
+ <!ELEMENT loose (hop)+>
+ <!ATTLIST loose
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "3"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT hop EMPTY>
+ <!ATTLIST hop
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT strict (hop)+>
+ <!ATTLIST strict
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "9"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT record (hop)+>
+ <!ATTLIST record
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "7"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT stream EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST stream
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "8"
+ length CDATA #FIXED "4"
+ id %Digits; #REQUIRED>
+
+ <!ELEMENT timestamp (tstamp)+>
+ <!-- 0 <= oflw <=15 -->
+
+
+
+Kennedy Informational [Page 10]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST timestamp
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "2"
+ number CDATA #FIXED "4"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED
+ oflw %Digits; #REQUIRED
+ flag (0 | 1 | 3) #REQUIRED>
+
+ <!ELEMENT tstamp EMPTY>
+ <!ATTLIST tstamp
+ time %Digits; #REQUIRED
+ address %IP4Addr; #IMPLIED>
+ <!--
+ padding to bring header to 32-bit boundary.
+ pad MUST be "0"*
+ -->
+ <!ELEMENT padding EMPTY>
+ <!ATTLIST padding
+ pad CDATA #REQUIRED>
+
+ <!-- payload MUST be encoded as base-64 [RFC2045], as modified
+ by section 2.1 of this RFC -->
+ <!ELEMENT payload (CDATA)>
+
+7.2. TCPoXML DTD
+
+ <!--
+ DTD for TCP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ -->
+
+ <!-- the pseudoheader is only included for checksum calculations -->
+ <!ELEMENT tcp (tcp.pseudoheader?, tcp.header, payload)>
+
+ <!ELEMENT tcp.header (src, dest, sequence, acknowledgement, offset,
+ reserved, control, window, checksum, urgent,
+ tcp.options, padding)>
+
+ <!ELEMENT src EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+ <!ATTLIST src
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT dest EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+
+
+
+Kennedy Informational [Page 11]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST dest
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT sequence EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST sequence
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT acknowledgement EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST acknowledgement
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= number <= 255 -->
+ <!ATTLIST offset
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT reserved EMPTY>
+ <!ATTLIST reserved
+ value CDATA #FIXED "0">
+
+ <!ELEMENT control EMPTY>
+ <!ATTLIST control
+ urg (0|1) #IMPLIED
+ ack (0|1) #IMPLIED
+ psh (0|1) #IMPLIED
+ rst (0|1) #IMPLIED
+ syn (0|1) #IMPLIED
+ fin (0|1) #IMPLIED>
+
+ <!ELEMENT window EMPTY>
+ <!-- 0 <= size <= 65,535 -->
+ <!ATTLIST window
+ size %Digits; #REQUIRED>
+
+ <!--
+ checksum as in ip, but with
+ the following pseudo-header added into the tcp element:
+ -->
+ <!ELEMENT tcp.pseudoheader (source, destination, protocol,
+ tcp.length)>
+
+ <!--
+ tcp header + data length in octets. does not include the size of
+
+ the pseudoheader.
+ -->
+
+
+
+Kennedy Informational [Page 12]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ELEMENT tcp.length EMPTY>
+ <!ATTLIST tcp.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT urgent EMPTY>
+ <!-- 0 <= pointer <= 65,535 -->
+ <!ATTLIST urgent
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT tcp.options (tcp.end | tcp.noop | tcp.mss)+>
+
+ <!ELEMENT tcp.end EMPTY>
+ <!ATTLIST tcp.end
+ kind CDATA #FIXED "0">
+
+ <!ELEMENT tcp.noop EMPTY>
+ <!ATTLIST tcp.noop
+ kind CDATA #FIXED "1">
+
+ <!ELEMENT tcp.mss EMPTY>
+ <!ATTLIST tcp.mss
+ kind CDATA #FIXED "2"
+ length CDATA #FIXED "4"
+ size %Digits; #REQUIRED>
+
+7.3. UDPoXML DTD
+
+ <!--
+ DTD for UDP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ -->
+
+ <!ELEMENT udp (udp.pseudoheader?, udp.header, payload)>
+
+ <!ELEMENT udp.header (src, dest, udp.length, checksum)>
+
+ <!ELEMENT udp.pseudoheader (source, destination, protocol,
+ udp.length)>
+
+ <!--
+ udp header + data length in octets. does not include the size of
+ the pseudoheader.
+ -->
+ <!ELEMENT udp.length EMPTY>
+ <!ATTLIST udp.length
+ value %Digits; #REQUIRED>
+
+
+
+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:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ <ip>
+ <header length="474">
+ <version value="4"/>
+ <tos precedence="Routine" delay="Normal" throughput="Normal"
+ relibility="Normal" reserved="0"/>
+ <total.length value="461"/>
+ <id value="1"/>
+ <flags reserved="0" df="dont" mf="last"/>
+ <offset value="0"/>
+ <ttl value="255"/>
+ <protocol value="6"/>
+ <checksum value="8707"/>
+ <source address="10.0.0.22"/>
+ <destination address="10.0.0.1"/>
+ <options>
+ <end copied="0" class="0" number="0"/>
+ </options>
+ <padding pad="0"/>
+ </header>
+ <payload>
+ </payload>
+ </ip>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+3.2. Example Datagram
+
+ The following is an example TCPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ <tcp>
+ <tcp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <sequence number="322622954"/>
+ <acknowledgement number="689715995"/>
+ <offset number=""/>
+ <reserved value="0"/>
+ <control syn="1" ack="1"/>
+ <window size="1"/>
+ <urgent pointer="0"/>
+ <checksum value="2988"/>
+ <tcp.options>
+ <tcp.end kind="0"/>
+ </tcp.options>
+ <padding pad="0"/>
+ </tcp.header>
+ <payload>
+ </payload>
+ </tcp>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+4.2. Example Datagram
+
+ The following is an example UDPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ <udp>
+ <udp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <udp.length value="143"/>
+ <checksum value="2988"/>
+ </udp.header>
+ <payload>
+ </payload>
+ </udp>
+
+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 ("<?xml".)
+
+5.2. IEEE 802
+
+ BLOAT is encapsulated in IEEE 802 Networks as in [RFC1042] except
+ that the protocol type code for IPoXML is 0xBEEF.
+
+
+
+
+
+Kennedy Informational [Page 6]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+6. Gatewaying over IP
+
+ In order to facilitate the gradual introduction of BLOAT into the
+ public Internet, BLOAT MAY be encapsulated in IP as in [RFC2003] to
+ gateway between networks that run BLOAT natively on their LANs.
+
+7. DTDs
+
+ The Transport DTDs (7.2. and 7.3.) build on the definitions in the
+ Network DTD (7.1.)
+
+ The DTDs are referenced by their PubidLiteral and SystemLiteral (from
+ [XML]) although it is understood that most IPoXML implementations
+ will not need to pull down the DTD, as it will normally be embedded
+ in the implementation, and presents something of a catch-22 if you
+ need to load part of your network protocol over the network.
+
+7.1. IPoXML DTD
+
+ <!--
+ DTD for IP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ -->
+ <!--
+ DTD data types:
+
+ Digits [0..9]+
+
+ Precedence "NetworkControl | InternetworkControl |
+ CRITIC | FlashOverride | Flash | Immediate |
+ Priority | Routine"
+
+ IP4Addr "dotted-decimal" notation of [RFC1123]
+
+ Class [0..3]
+
+ Sec "Unclassified | Confidential | EFTO | MMMM | PROG |
+ Restricted | Secret | Top Secret | Reserved"
+
+ Compartments [0..65535]
+
+ Handling [0..65535]
+
+ TCC [0..16777216]
+
+ -->
+
+
+
+Kennedy Informational [Page 7]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ENTITY % Digits "CDATA">
+ <!ENTITY % Precedence "CDATA">
+ <!ENTITY % IP4Addr "CDATA">
+ <!ENTITY % Class "CDATA">
+ <!ENTITY % Sec "CDATA">
+ <!ENTITY % Compartments "CDATA">
+ <!ENTITY % Handling "CDATA">
+ <!ENTITY % TCC "CDATA">
+
+ <!ELEMENT ip (header, payload)>
+
+ <!ELEMENT header (version, tos, total.length, id, flags, offset, ttl,
+ protocol, checksum, source, destination, options,
+ padding)>
+ <!-- length of header in 32-bit words -->
+ <!ATTLIST header
+ length %Digits; #REQUIRED>
+
+ <!ELEMENT version EMPTY>
+ <!-- ip version. SHOULD be "4" -->
+ <!ATTLIST version
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT tos EMPTY>
+ <!ATTLIST tos
+ precedence %Precedence; #REQUIRED
+ delay (normal | low) #REQUIRED
+ throughput (normal | high) #REQUIRED
+ relibility (normal | high) #REQUIRED
+ reserved CDATA #FIXED "0">
+
+ <!ELEMENT total.length EMPTY>
+ <!--
+ total length of datagram (header and payload) in octets, MUST be
+ less than 65,535 (and SHOULD be less than 1024 for IPoXML on local
+ ethernets).
+ -->
+ <!ATTLIST total.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT id EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST id
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT flags EMPTY>
+ <!-- df = don't fragment, mf = more fragments -->
+ <!ATTLIST flags
+
+
+
+Kennedy Informational [Page 8]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ reserved CDATA #FIXED "0"
+ df (may|dont) #REQUIRED
+ mf (last|more) #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= offset <= 8192 measured in 8 octet (64-bit) chunks -->
+ <!ATTLIST offset
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT ttl EMPTY>
+ <!-- 0 <= ttl <= 255 -->
+ <!ATTLIST ttl
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT protocol EMPTY>
+ <!-- 0 <= protocol <= 255 (per IANA) -->
+ <!ATTLIST protocol
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT checksum EMPTY>
+ <!-- 0 <= checksum <= 65535 (over header only) -->
+ <!ATTLIST checksum
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT source EMPTY>
+ <!ATTLIST source
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT destination EMPTY>
+ <!ATTLIST destination
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT options ( end | noop | security | loose | strict | record
+ | stream | timestamp )*>
+
+ <!ELEMENT end EMPTY>
+ <!ATTLIST end
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "0">
+
+ <!ELEMENT noop EMPTY>
+ <!ATTLIST noop
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "1">
+
+ <!ELEMENT security EMPTY>
+
+
+
+Kennedy Informational [Page 9]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST security
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "2"
+ length CDATA #FIXED "11"
+ security %Sec; #REQUIRED
+ compartments %Compartments; #REQUIRED
+ handling %Handling; #REQUIRED
+ tcc %TCC; #REQUIRED>
+ <!ELEMENT loose (hop)+>
+ <!ATTLIST loose
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "3"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT hop EMPTY>
+ <!ATTLIST hop
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT strict (hop)+>
+ <!ATTLIST strict
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "9"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT record (hop)+>
+ <!ATTLIST record
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "7"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT stream EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST stream
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "8"
+ length CDATA #FIXED "4"
+ id %Digits; #REQUIRED>
+
+ <!ELEMENT timestamp (tstamp)+>
+ <!-- 0 <= oflw <=15 -->
+
+
+
+Kennedy Informational [Page 10]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST timestamp
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "2"
+ number CDATA #FIXED "4"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED
+ oflw %Digits; #REQUIRED
+ flag (0 | 1 | 3) #REQUIRED>
+
+ <!ELEMENT tstamp EMPTY>
+ <!ATTLIST tstamp
+ time %Digits; #REQUIRED
+ address %IP4Addr; #IMPLIED>
+ <!--
+ padding to bring header to 32-bit boundary.
+ pad MUST be "0"*
+ -->
+ <!ELEMENT padding EMPTY>
+ <!ATTLIST padding
+ pad CDATA #REQUIRED>
+
+ <!-- payload MUST be encoded as base-64 [RFC2045], as modified
+ by section 2.1 of this RFC -->
+ <!ELEMENT payload (CDATA)>
+
+7.2. TCPoXML DTD
+
+ <!--
+ DTD for TCP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ -->
+
+ <!-- the pseudoheader is only included for checksum calculations -->
+ <!ELEMENT tcp (tcp.pseudoheader?, tcp.header, payload)>
+
+ <!ELEMENT tcp.header (src, dest, sequence, acknowledgement, offset,
+ reserved, control, window, checksum, urgent,
+ tcp.options, padding)>
+
+ <!ELEMENT src EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+ <!ATTLIST src
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT dest EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+
+
+
+Kennedy Informational [Page 11]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST dest
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT sequence EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST sequence
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT acknowledgement EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST acknowledgement
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= number <= 255 -->
+ <!ATTLIST offset
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT reserved EMPTY>
+ <!ATTLIST reserved
+ value CDATA #FIXED "0">
+
+ <!ELEMENT control EMPTY>
+ <!ATTLIST control
+ urg (0|1) #IMPLIED
+ ack (0|1) #IMPLIED
+ psh (0|1) #IMPLIED
+ rst (0|1) #IMPLIED
+ syn (0|1) #IMPLIED
+ fin (0|1) #IMPLIED>
+
+ <!ELEMENT window EMPTY>
+ <!-- 0 <= size <= 65,535 -->
+ <!ATTLIST window
+ size %Digits; #REQUIRED>
+
+ <!--
+ checksum as in ip, but with
+ the following pseudo-header added into the tcp element:
+ -->
+ <!ELEMENT tcp.pseudoheader (source, destination, protocol,
+ tcp.length)>
+
+ <!--
+ tcp header + data length in octets. does not include the size of
+
+ the pseudoheader.
+ -->
+
+
+
+Kennedy Informational [Page 12]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ELEMENT tcp.length EMPTY>
+ <!ATTLIST tcp.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT urgent EMPTY>
+ <!-- 0 <= pointer <= 65,535 -->
+ <!ATTLIST urgent
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT tcp.options (tcp.end | tcp.noop | tcp.mss)+>
+
+ <!ELEMENT tcp.end EMPTY>
+ <!ATTLIST tcp.end
+ kind CDATA #FIXED "0">
+
+ <!ELEMENT tcp.noop EMPTY>
+ <!ATTLIST tcp.noop
+ kind CDATA #FIXED "1">
+
+ <!ELEMENT tcp.mss EMPTY>
+ <!ATTLIST tcp.mss
+ kind CDATA #FIXED "2"
+ length CDATA #FIXED "4"
+ size %Digits; #REQUIRED>
+
+7.3. UDPoXML DTD
+
+ <!--
+ DTD for UDP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ -->
+
+ <!ELEMENT udp (udp.pseudoheader?, udp.header, payload)>
+
+ <!ELEMENT udp.header (src, dest, udp.length, checksum)>
+
+ <!ELEMENT udp.pseudoheader (source, destination, protocol,
+ udp.length)>
+
+ <!--
+ udp header + data length in octets. does not include the size of
+ the pseudoheader.
+ -->
+ <!ELEMENT udp.length EMPTY>
+ <!ATTLIST udp.length
+ value %Digits; #REQUIRED>
+
+
+
+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:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ <ip>
+ <header length="474">
+ <version value="4"/>
+ <tos precedence="Routine" delay="Normal" throughput="Normal"
+ relibility="Normal" reserved="0"/>
+ <total.length value="461"/>
+ <id value="1"/>
+ <flags reserved="0" df="dont" mf="last"/>
+ <offset value="0"/>
+ <ttl value="255"/>
+ <protocol value="6"/>
+ <checksum value="8707"/>
+ <source address="10.0.0.22"/>
+ <destination address="10.0.0.1"/>
+ <options>
+ <end copied="0" class="0" number="0"/>
+ </options>
+ <padding pad="0"/>
+ </header>
+ <payload>
+ </payload>
+ </ip>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+3.2. Example Datagram
+
+ The following is an example TCPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ <tcp>
+ <tcp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <sequence number="322622954"/>
+ <acknowledgement number="689715995"/>
+ <offset number=""/>
+ <reserved value="0"/>
+ <control syn="1" ack="1"/>
+ <window size="1"/>
+ <urgent pointer="0"/>
+ <checksum value="2988"/>
+ <tcp.options>
+ <tcp.end kind="0"/>
+ </tcp.options>
+ <padding pad="0"/>
+ </tcp.header>
+ <payload>
+ </payload>
+ </tcp>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+4.2. Example Datagram
+
+ The following is an example UDPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ <udp>
+ <udp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <udp.length value="143"/>
+ <checksum value="2988"/>
+ </udp.header>
+ <payload>
+ </payload>
+ </udp>
+
+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 ("<?xml".)
+
+5.2. IEEE 802
+
+ BLOAT is encapsulated in IEEE 802 Networks as in [RFC1042] except
+ that the protocol type code for IPoXML is 0xBEEF.
+
+
+
+
+
+Kennedy Informational [Page 6]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+6. Gatewaying over IP
+
+ In order to facilitate the gradual introduction of BLOAT into the
+ public Internet, BLOAT MAY be encapsulated in IP as in [RFC2003] to
+ gateway between networks that run BLOAT natively on their LANs.
+
+7. DTDs
+
+ The Transport DTDs (7.2. and 7.3.) build on the definitions in the
+ Network DTD (7.1.)
+
+ The DTDs are referenced by their PubidLiteral and SystemLiteral (from
+ [XML]) although it is understood that most IPoXML implementations
+ will not need to pull down the DTD, as it will normally be embedded
+ in the implementation, and presents something of a catch-22 if you
+ need to load part of your network protocol over the network.
+
+7.1. IPoXML DTD
+
+ <!--
+ DTD for IP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ -->
+ <!--
+ DTD data types:
+
+ Digits [0..9]+
+
+ Precedence "NetworkControl | InternetworkControl |
+ CRITIC | FlashOverride | Flash | Immediate |
+ Priority | Routine"
+
+ IP4Addr "dotted-decimal" notation of [RFC1123]
+
+ Class [0..3]
+
+ Sec "Unclassified | Confidential | EFTO | MMMM | PROG |
+ Restricted | Secret | Top Secret | Reserved"
+
+ Compartments [0..65535]
+
+ Handling [0..65535]
+
+ TCC [0..16777216]
+
+ -->
+
+
+
+Kennedy Informational [Page 7]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ENTITY % Digits "CDATA">
+ <!ENTITY % Precedence "CDATA">
+ <!ENTITY % IP4Addr "CDATA">
+ <!ENTITY % Class "CDATA">
+ <!ENTITY % Sec "CDATA">
+ <!ENTITY % Compartments "CDATA">
+ <!ENTITY % Handling "CDATA">
+ <!ENTITY % TCC "CDATA">
+
+ <!ELEMENT ip (header, payload)>
+
+ <!ELEMENT header (version, tos, total.length, id, flags, offset, ttl,
+ protocol, checksum, source, destination, options,
+ padding)>
+ <!-- length of header in 32-bit words -->
+ <!ATTLIST header
+ length %Digits; #REQUIRED>
+
+ <!ELEMENT version EMPTY>
+ <!-- ip version. SHOULD be "4" -->
+ <!ATTLIST version
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT tos EMPTY>
+ <!ATTLIST tos
+ precedence %Precedence; #REQUIRED
+ delay (normal | low) #REQUIRED
+ throughput (normal | high) #REQUIRED
+ relibility (normal | high) #REQUIRED
+ reserved CDATA #FIXED "0">
+
+ <!ELEMENT total.length EMPTY>
+ <!--
+ total length of datagram (header and payload) in octets, MUST be
+ less than 65,535 (and SHOULD be less than 1024 for IPoXML on local
+ ethernets).
+ -->
+ <!ATTLIST total.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT id EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST id
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT flags EMPTY>
+ <!-- df = don't fragment, mf = more fragments -->
+ <!ATTLIST flags
+
+
+
+Kennedy Informational [Page 8]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ reserved CDATA #FIXED "0"
+ df (may|dont) #REQUIRED
+ mf (last|more) #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= offset <= 8192 measured in 8 octet (64-bit) chunks -->
+ <!ATTLIST offset
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT ttl EMPTY>
+ <!-- 0 <= ttl <= 255 -->
+ <!ATTLIST ttl
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT protocol EMPTY>
+ <!-- 0 <= protocol <= 255 (per IANA) -->
+ <!ATTLIST protocol
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT checksum EMPTY>
+ <!-- 0 <= checksum <= 65535 (over header only) -->
+ <!ATTLIST checksum
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT source EMPTY>
+ <!ATTLIST source
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT destination EMPTY>
+ <!ATTLIST destination
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT options ( end | noop | security | loose | strict | record
+ | stream | timestamp )*>
+
+ <!ELEMENT end EMPTY>
+ <!ATTLIST end
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "0">
+
+ <!ELEMENT noop EMPTY>
+ <!ATTLIST noop
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "1">
+
+ <!ELEMENT security EMPTY>
+
+
+
+Kennedy Informational [Page 9]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST security
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "2"
+ length CDATA #FIXED "11"
+ security %Sec; #REQUIRED
+ compartments %Compartments; #REQUIRED
+ handling %Handling; #REQUIRED
+ tcc %TCC; #REQUIRED>
+ <!ELEMENT loose (hop)+>
+ <!ATTLIST loose
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "3"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT hop EMPTY>
+ <!ATTLIST hop
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT strict (hop)+>
+ <!ATTLIST strict
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "9"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT record (hop)+>
+ <!ATTLIST record
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "7"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT stream EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST stream
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "8"
+ length CDATA #FIXED "4"
+ id %Digits; #REQUIRED>
+
+ <!ELEMENT timestamp (tstamp)+>
+ <!-- 0 <= oflw <=15 -->
+
+
+
+Kennedy Informational [Page 10]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST timestamp
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "2"
+ number CDATA #FIXED "4"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED
+ oflw %Digits; #REQUIRED
+ flag (0 | 1 | 3) #REQUIRED>
+
+ <!ELEMENT tstamp EMPTY>
+ <!ATTLIST tstamp
+ time %Digits; #REQUIRED
+ address %IP4Addr; #IMPLIED>
+ <!--
+ padding to bring header to 32-bit boundary.
+ pad MUST be "0"*
+ -->
+ <!ELEMENT padding EMPTY>
+ <!ATTLIST padding
+ pad CDATA #REQUIRED>
+
+ <!-- payload MUST be encoded as base-64 [RFC2045], as modified
+ by section 2.1 of this RFC -->
+ <!ELEMENT payload (CDATA)>
+
+7.2. TCPoXML DTD
+
+ <!--
+ DTD for TCP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ -->
+
+ <!-- the pseudoheader is only included for checksum calculations -->
+ <!ELEMENT tcp (tcp.pseudoheader?, tcp.header, payload)>
+
+ <!ELEMENT tcp.header (src, dest, sequence, acknowledgement, offset,
+ reserved, control, window, checksum, urgent,
+ tcp.options, padding)>
+
+ <!ELEMENT src EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+ <!ATTLIST src
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT dest EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+
+
+
+Kennedy Informational [Page 11]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST dest
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT sequence EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST sequence
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT acknowledgement EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST acknowledgement
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= number <= 255 -->
+ <!ATTLIST offset
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT reserved EMPTY>
+ <!ATTLIST reserved
+ value CDATA #FIXED "0">
+
+ <!ELEMENT control EMPTY>
+ <!ATTLIST control
+ urg (0|1) #IMPLIED
+ ack (0|1) #IMPLIED
+ psh (0|1) #IMPLIED
+ rst (0|1) #IMPLIED
+ syn (0|1) #IMPLIED
+ fin (0|1) #IMPLIED>
+
+ <!ELEMENT window EMPTY>
+ <!-- 0 <= size <= 65,535 -->
+ <!ATTLIST window
+ size %Digits; #REQUIRED>
+
+ <!--
+ checksum as in ip, but with
+ the following pseudo-header added into the tcp element:
+ -->
+ <!ELEMENT tcp.pseudoheader (source, destination, protocol,
+ tcp.length)>
+
+ <!--
+ tcp header + data length in octets. does not include the size of
+
+ the pseudoheader.
+ -->
+
+
+
+Kennedy Informational [Page 12]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ELEMENT tcp.length EMPTY>
+ <!ATTLIST tcp.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT urgent EMPTY>
+ <!-- 0 <= pointer <= 65,535 -->
+ <!ATTLIST urgent
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT tcp.options (tcp.end | tcp.noop | tcp.mss)+>
+
+ <!ELEMENT tcp.end EMPTY>
+ <!ATTLIST tcp.end
+ kind CDATA #FIXED "0">
+
+ <!ELEMENT tcp.noop EMPTY>
+ <!ATTLIST tcp.noop
+ kind CDATA #FIXED "1">
+
+ <!ELEMENT tcp.mss EMPTY>
+ <!ATTLIST tcp.mss
+ kind CDATA #FIXED "2"
+ length CDATA #FIXED "4"
+ size %Digits; #REQUIRED>
+
+7.3. UDPoXML DTD
+
+ <!--
+ DTD for UDP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ -->
+
+ <!ELEMENT udp (udp.pseudoheader?, udp.header, payload)>
+
+ <!ELEMENT udp.header (src, dest, udp.length, checksum)>
+
+ <!ELEMENT udp.pseudoheader (source, destination, protocol,
+ udp.length)>
+
+ <!--
+ udp header + data length in octets. does not include the size of
+ the pseudoheader.
+ -->
+ <!ELEMENT udp.length EMPTY>
+ <!ATTLIST udp.length
+ value %Digits; #REQUIRED>
+
+
+
+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:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ <ip>
+ <header length="474">
+ <version value="4"/>
+ <tos precedence="Routine" delay="Normal" throughput="Normal"
+ relibility="Normal" reserved="0"/>
+ <total.length value="461"/>
+ <id value="1"/>
+ <flags reserved="0" df="dont" mf="last"/>
+ <offset value="0"/>
+ <ttl value="255"/>
+ <protocol value="6"/>
+ <checksum value="8707"/>
+ <source address="10.0.0.22"/>
+ <destination address="10.0.0.1"/>
+ <options>
+ <end copied="0" class="0" number="0"/>
+ </options>
+ <padding pad="0"/>
+ </header>
+ <payload>
+ </payload>
+ </ip>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+3.2. Example Datagram
+
+ The following is an example TCPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ <tcp>
+ <tcp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <sequence number="322622954"/>
+ <acknowledgement number="689715995"/>
+ <offset number=""/>
+ <reserved value="0"/>
+ <control syn="1" ack="1"/>
+ <window size="1"/>
+ <urgent pointer="0"/>
+ <checksum value="2988"/>
+ <tcp.options>
+ <tcp.end kind="0"/>
+ </tcp.options>
+ <padding pad="0"/>
+ </tcp.header>
+ <payload>
+ </payload>
+ </tcp>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+4.2. Example Datagram
+
+ The following is an example UDPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ <udp>
+ <udp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <udp.length value="143"/>
+ <checksum value="2988"/>
+ </udp.header>
+ <payload>
+ </payload>
+ </udp>
+
+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 ("<?xml".)
+
+5.2. IEEE 802
+
+ BLOAT is encapsulated in IEEE 802 Networks as in [RFC1042] except
+ that the protocol type code for IPoXML is 0xBEEF.
+
+
+
+
+
+Kennedy Informational [Page 6]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+6. Gatewaying over IP
+
+ In order to facilitate the gradual introduction of BLOAT into the
+ public Internet, BLOAT MAY be encapsulated in IP as in [RFC2003] to
+ gateway between networks that run BLOAT natively on their LANs.
+
+7. DTDs
+
+ The Transport DTDs (7.2. and 7.3.) build on the definitions in the
+ Network DTD (7.1.)
+
+ The DTDs are referenced by their PubidLiteral and SystemLiteral (from
+ [XML]) although it is understood that most IPoXML implementations
+ will not need to pull down the DTD, as it will normally be embedded
+ in the implementation, and presents something of a catch-22 if you
+ need to load part of your network protocol over the network.
+
+7.1. IPoXML DTD
+
+ <!--
+ DTD for IP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ -->
+ <!--
+ DTD data types:
+
+ Digits [0..9]+
+
+ Precedence "NetworkControl | InternetworkControl |
+ CRITIC | FlashOverride | Flash | Immediate |
+ Priority | Routine"
+
+ IP4Addr "dotted-decimal" notation of [RFC1123]
+
+ Class [0..3]
+
+ Sec "Unclassified | Confidential | EFTO | MMMM | PROG |
+ Restricted | Secret | Top Secret | Reserved"
+
+ Compartments [0..65535]
+
+ Handling [0..65535]
+
+ TCC [0..16777216]
+
+ -->
+
+
+
+Kennedy Informational [Page 7]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ENTITY % Digits "CDATA">
+ <!ENTITY % Precedence "CDATA">
+ <!ENTITY % IP4Addr "CDATA">
+ <!ENTITY % Class "CDATA">
+ <!ENTITY % Sec "CDATA">
+ <!ENTITY % Compartments "CDATA">
+ <!ENTITY % Handling "CDATA">
+ <!ENTITY % TCC "CDATA">
+
+ <!ELEMENT ip (header, payload)>
+
+ <!ELEMENT header (version, tos, total.length, id, flags, offset, ttl,
+ protocol, checksum, source, destination, options,
+ padding)>
+ <!-- length of header in 32-bit words -->
+ <!ATTLIST header
+ length %Digits; #REQUIRED>
+
+ <!ELEMENT version EMPTY>
+ <!-- ip version. SHOULD be "4" -->
+ <!ATTLIST version
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT tos EMPTY>
+ <!ATTLIST tos
+ precedence %Precedence; #REQUIRED
+ delay (normal | low) #REQUIRED
+ throughput (normal | high) #REQUIRED
+ relibility (normal | high) #REQUIRED
+ reserved CDATA #FIXED "0">
+
+ <!ELEMENT total.length EMPTY>
+ <!--
+ total length of datagram (header and payload) in octets, MUST be
+ less than 65,535 (and SHOULD be less than 1024 for IPoXML on local
+ ethernets).
+ -->
+ <!ATTLIST total.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT id EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST id
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT flags EMPTY>
+ <!-- df = don't fragment, mf = more fragments -->
+ <!ATTLIST flags
+
+
+
+Kennedy Informational [Page 8]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ reserved CDATA #FIXED "0"
+ df (may|dont) #REQUIRED
+ mf (last|more) #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= offset <= 8192 measured in 8 octet (64-bit) chunks -->
+ <!ATTLIST offset
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT ttl EMPTY>
+ <!-- 0 <= ttl <= 255 -->
+ <!ATTLIST ttl
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT protocol EMPTY>
+ <!-- 0 <= protocol <= 255 (per IANA) -->
+ <!ATTLIST protocol
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT checksum EMPTY>
+ <!-- 0 <= checksum <= 65535 (over header only) -->
+ <!ATTLIST checksum
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT source EMPTY>
+ <!ATTLIST source
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT destination EMPTY>
+ <!ATTLIST destination
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT options ( end | noop | security | loose | strict | record
+ | stream | timestamp )*>
+
+ <!ELEMENT end EMPTY>
+ <!ATTLIST end
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "0">
+
+ <!ELEMENT noop EMPTY>
+ <!ATTLIST noop
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "1">
+
+ <!ELEMENT security EMPTY>
+
+
+
+Kennedy Informational [Page 9]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST security
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "2"
+ length CDATA #FIXED "11"
+ security %Sec; #REQUIRED
+ compartments %Compartments; #REQUIRED
+ handling %Handling; #REQUIRED
+ tcc %TCC; #REQUIRED>
+ <!ELEMENT loose (hop)+>
+ <!ATTLIST loose
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "3"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT hop EMPTY>
+ <!ATTLIST hop
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT strict (hop)+>
+ <!ATTLIST strict
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "9"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT record (hop)+>
+ <!ATTLIST record
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "7"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT stream EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST stream
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "8"
+ length CDATA #FIXED "4"
+ id %Digits; #REQUIRED>
+
+ <!ELEMENT timestamp (tstamp)+>
+ <!-- 0 <= oflw <=15 -->
+
+
+
+Kennedy Informational [Page 10]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST timestamp
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "2"
+ number CDATA #FIXED "4"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED
+ oflw %Digits; #REQUIRED
+ flag (0 | 1 | 3) #REQUIRED>
+
+ <!ELEMENT tstamp EMPTY>
+ <!ATTLIST tstamp
+ time %Digits; #REQUIRED
+ address %IP4Addr; #IMPLIED>
+ <!--
+ padding to bring header to 32-bit boundary.
+ pad MUST be "0"*
+ -->
+ <!ELEMENT padding EMPTY>
+ <!ATTLIST padding
+ pad CDATA #REQUIRED>
+
+ <!-- payload MUST be encoded as base-64 [RFC2045], as modified
+ by section 2.1 of this RFC -->
+ <!ELEMENT payload (CDATA)>
+
+7.2. TCPoXML DTD
+
+ <!--
+ DTD for TCP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ -->
+
+ <!-- the pseudoheader is only included for checksum calculations -->
+ <!ELEMENT tcp (tcp.pseudoheader?, tcp.header, payload)>
+
+ <!ELEMENT tcp.header (src, dest, sequence, acknowledgement, offset,
+ reserved, control, window, checksum, urgent,
+ tcp.options, padding)>
+
+ <!ELEMENT src EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+ <!ATTLIST src
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT dest EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+
+
+
+Kennedy Informational [Page 11]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST dest
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT sequence EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST sequence
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT acknowledgement EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST acknowledgement
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= number <= 255 -->
+ <!ATTLIST offset
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT reserved EMPTY>
+ <!ATTLIST reserved
+ value CDATA #FIXED "0">
+
+ <!ELEMENT control EMPTY>
+ <!ATTLIST control
+ urg (0|1) #IMPLIED
+ ack (0|1) #IMPLIED
+ psh (0|1) #IMPLIED
+ rst (0|1) #IMPLIED
+ syn (0|1) #IMPLIED
+ fin (0|1) #IMPLIED>
+
+ <!ELEMENT window EMPTY>
+ <!-- 0 <= size <= 65,535 -->
+ <!ATTLIST window
+ size %Digits; #REQUIRED>
+
+ <!--
+ checksum as in ip, but with
+ the following pseudo-header added into the tcp element:
+ -->
+ <!ELEMENT tcp.pseudoheader (source, destination, protocol,
+ tcp.length)>
+
+ <!--
+ tcp header + data length in octets. does not include the size of
+
+ the pseudoheader.
+ -->
+
+
+
+Kennedy Informational [Page 12]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ELEMENT tcp.length EMPTY>
+ <!ATTLIST tcp.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT urgent EMPTY>
+ <!-- 0 <= pointer <= 65,535 -->
+ <!ATTLIST urgent
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT tcp.options (tcp.end | tcp.noop | tcp.mss)+>
+
+ <!ELEMENT tcp.end EMPTY>
+ <!ATTLIST tcp.end
+ kind CDATA #FIXED "0">
+
+ <!ELEMENT tcp.noop EMPTY>
+ <!ATTLIST tcp.noop
+ kind CDATA #FIXED "1">
+
+ <!ELEMENT tcp.mss EMPTY>
+ <!ATTLIST tcp.mss
+ kind CDATA #FIXED "2"
+ length CDATA #FIXED "4"
+ size %Digits; #REQUIRED>
+
+7.3. UDPoXML DTD
+
+ <!--
+ DTD for UDP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ -->
+
+ <!ELEMENT udp (udp.pseudoheader?, udp.header, payload)>
+
+ <!ELEMENT udp.header (src, dest, udp.length, checksum)>
+
+ <!ELEMENT udp.pseudoheader (source, destination, protocol,
+ udp.length)>
+
+ <!--
+ udp header + data length in octets. does not include the size of
+ the pseudoheader.
+ -->
+ <!ELEMENT udp.length EMPTY>
+ <!ATTLIST udp.length
+ value %Digits; #REQUIRED>
+
+
+
+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:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ <ip>
+ <header length="474">
+ <version value="4"/>
+ <tos precedence="Routine" delay="Normal" throughput="Normal"
+ relibility="Normal" reserved="0"/>
+ <total.length value="461"/>
+ <id value="1"/>
+ <flags reserved="0" df="dont" mf="last"/>
+ <offset value="0"/>
+ <ttl value="255"/>
+ <protocol value="6"/>
+ <checksum value="8707"/>
+ <source address="10.0.0.22"/>
+ <destination address="10.0.0.1"/>
+ <options>
+ <end copied="0" class="0" number="0"/>
+ </options>
+ <padding pad="0"/>
+ </header>
+ <payload>
+ </payload>
+ </ip>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+3.2. Example Datagram
+
+ The following is an example TCPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ <tcp>
+ <tcp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <sequence number="322622954"/>
+ <acknowledgement number="689715995"/>
+ <offset number=""/>
+ <reserved value="0"/>
+ <control syn="1" ack="1"/>
+ <window size="1"/>
+ <urgent pointer="0"/>
+ <checksum value="2988"/>
+ <tcp.options>
+ <tcp.end kind="0"/>
+ </tcp.options>
+ <padding pad="0"/>
+ </tcp.header>
+ <payload>
+ </payload>
+ </tcp>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+4.2. Example Datagram
+
+ The following is an example UDPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ <udp>
+ <udp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <udp.length value="143"/>
+ <checksum value="2988"/>
+ </udp.header>
+ <payload>
+ </payload>
+ </udp>
+
+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 ("<?xml".)
+
+5.2. IEEE 802
+
+ BLOAT is encapsulated in IEEE 802 Networks as in [RFC1042] except
+ that the protocol type code for IPoXML is 0xBEEF.
+
+
+
+
+
+Kennedy Informational [Page 6]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+6. Gatewaying over IP
+
+ In order to facilitate the gradual introduction of BLOAT into the
+ public Internet, BLOAT MAY be encapsulated in IP as in [RFC2003] to
+ gateway between networks that run BLOAT natively on their LANs.
+
+7. DTDs
+
+ The Transport DTDs (7.2. and 7.3.) build on the definitions in the
+ Network DTD (7.1.)
+
+ The DTDs are referenced by their PubidLiteral and SystemLiteral (from
+ [XML]) although it is understood that most IPoXML implementations
+ will not need to pull down the DTD, as it will normally be embedded
+ in the implementation, and presents something of a catch-22 if you
+ need to load part of your network protocol over the network.
+
+7.1. IPoXML DTD
+
+ <!--
+ DTD for IP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ -->
+ <!--
+ DTD data types:
+
+ Digits [0..9]+
+
+ Precedence "NetworkControl | InternetworkControl |
+ CRITIC | FlashOverride | Flash | Immediate |
+ Priority | Routine"
+
+ IP4Addr "dotted-decimal" notation of [RFC1123]
+
+ Class [0..3]
+
+ Sec "Unclassified | Confidential | EFTO | MMMM | PROG |
+ Restricted | Secret | Top Secret | Reserved"
+
+ Compartments [0..65535]
+
+ Handling [0..65535]
+
+ TCC [0..16777216]
+
+ -->
+
+
+
+Kennedy Informational [Page 7]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ENTITY % Digits "CDATA">
+ <!ENTITY % Precedence "CDATA">
+ <!ENTITY % IP4Addr "CDATA">
+ <!ENTITY % Class "CDATA">
+ <!ENTITY % Sec "CDATA">
+ <!ENTITY % Compartments "CDATA">
+ <!ENTITY % Handling "CDATA">
+ <!ENTITY % TCC "CDATA">
+
+ <!ELEMENT ip (header, payload)>
+
+ <!ELEMENT header (version, tos, total.length, id, flags, offset, ttl,
+ protocol, checksum, source, destination, options,
+ padding)>
+ <!-- length of header in 32-bit words -->
+ <!ATTLIST header
+ length %Digits; #REQUIRED>
+
+ <!ELEMENT version EMPTY>
+ <!-- ip version. SHOULD be "4" -->
+ <!ATTLIST version
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT tos EMPTY>
+ <!ATTLIST tos
+ precedence %Precedence; #REQUIRED
+ delay (normal | low) #REQUIRED
+ throughput (normal | high) #REQUIRED
+ relibility (normal | high) #REQUIRED
+ reserved CDATA #FIXED "0">
+
+ <!ELEMENT total.length EMPTY>
+ <!--
+ total length of datagram (header and payload) in octets, MUST be
+ less than 65,535 (and SHOULD be less than 1024 for IPoXML on local
+ ethernets).
+ -->
+ <!ATTLIST total.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT id EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST id
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT flags EMPTY>
+ <!-- df = don't fragment, mf = more fragments -->
+ <!ATTLIST flags
+
+
+
+Kennedy Informational [Page 8]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ reserved CDATA #FIXED "0"
+ df (may|dont) #REQUIRED
+ mf (last|more) #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= offset <= 8192 measured in 8 octet (64-bit) chunks -->
+ <!ATTLIST offset
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT ttl EMPTY>
+ <!-- 0 <= ttl <= 255 -->
+ <!ATTLIST ttl
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT protocol EMPTY>
+ <!-- 0 <= protocol <= 255 (per IANA) -->
+ <!ATTLIST protocol
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT checksum EMPTY>
+ <!-- 0 <= checksum <= 65535 (over header only) -->
+ <!ATTLIST checksum
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT source EMPTY>
+ <!ATTLIST source
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT destination EMPTY>
+ <!ATTLIST destination
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT options ( end | noop | security | loose | strict | record
+ | stream | timestamp )*>
+
+ <!ELEMENT end EMPTY>
+ <!ATTLIST end
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "0">
+
+ <!ELEMENT noop EMPTY>
+ <!ATTLIST noop
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "1">
+
+ <!ELEMENT security EMPTY>
+
+
+
+Kennedy Informational [Page 9]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST security
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "2"
+ length CDATA #FIXED "11"
+ security %Sec; #REQUIRED
+ compartments %Compartments; #REQUIRED
+ handling %Handling; #REQUIRED
+ tcc %TCC; #REQUIRED>
+ <!ELEMENT loose (hop)+>
+ <!ATTLIST loose
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "3"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT hop EMPTY>
+ <!ATTLIST hop
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT strict (hop)+>
+ <!ATTLIST strict
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "9"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT record (hop)+>
+ <!ATTLIST record
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "7"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT stream EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST stream
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "8"
+ length CDATA #FIXED "4"
+ id %Digits; #REQUIRED>
+
+ <!ELEMENT timestamp (tstamp)+>
+ <!-- 0 <= oflw <=15 -->
+
+
+
+Kennedy Informational [Page 10]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST timestamp
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "2"
+ number CDATA #FIXED "4"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED
+ oflw %Digits; #REQUIRED
+ flag (0 | 1 | 3) #REQUIRED>
+
+ <!ELEMENT tstamp EMPTY>
+ <!ATTLIST tstamp
+ time %Digits; #REQUIRED
+ address %IP4Addr; #IMPLIED>
+ <!--
+ padding to bring header to 32-bit boundary.
+ pad MUST be "0"*
+ -->
+ <!ELEMENT padding EMPTY>
+ <!ATTLIST padding
+ pad CDATA #REQUIRED>
+
+ <!-- payload MUST be encoded as base-64 [RFC2045], as modified
+ by section 2.1 of this RFC -->
+ <!ELEMENT payload (CDATA)>
+
+7.2. TCPoXML DTD
+
+ <!--
+ DTD for TCP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ -->
+
+ <!-- the pseudoheader is only included for checksum calculations -->
+ <!ELEMENT tcp (tcp.pseudoheader?, tcp.header, payload)>
+
+ <!ELEMENT tcp.header (src, dest, sequence, acknowledgement, offset,
+ reserved, control, window, checksum, urgent,
+ tcp.options, padding)>
+
+ <!ELEMENT src EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+ <!ATTLIST src
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT dest EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+
+
+
+Kennedy Informational [Page 11]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST dest
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT sequence EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST sequence
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT acknowledgement EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST acknowledgement
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= number <= 255 -->
+ <!ATTLIST offset
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT reserved EMPTY>
+ <!ATTLIST reserved
+ value CDATA #FIXED "0">
+
+ <!ELEMENT control EMPTY>
+ <!ATTLIST control
+ urg (0|1) #IMPLIED
+ ack (0|1) #IMPLIED
+ psh (0|1) #IMPLIED
+ rst (0|1) #IMPLIED
+ syn (0|1) #IMPLIED
+ fin (0|1) #IMPLIED>
+
+ <!ELEMENT window EMPTY>
+ <!-- 0 <= size <= 65,535 -->
+ <!ATTLIST window
+ size %Digits; #REQUIRED>
+
+ <!--
+ checksum as in ip, but with
+ the following pseudo-header added into the tcp element:
+ -->
+ <!ELEMENT tcp.pseudoheader (source, destination, protocol,
+ tcp.length)>
+
+ <!--
+ tcp header + data length in octets. does not include the size of
+
+ the pseudoheader.
+ -->
+
+
+
+Kennedy Informational [Page 12]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ELEMENT tcp.length EMPTY>
+ <!ATTLIST tcp.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT urgent EMPTY>
+ <!-- 0 <= pointer <= 65,535 -->
+ <!ATTLIST urgent
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT tcp.options (tcp.end | tcp.noop | tcp.mss)+>
+
+ <!ELEMENT tcp.end EMPTY>
+ <!ATTLIST tcp.end
+ kind CDATA #FIXED "0">
+
+ <!ELEMENT tcp.noop EMPTY>
+ <!ATTLIST tcp.noop
+ kind CDATA #FIXED "1">
+
+ <!ELEMENT tcp.mss EMPTY>
+ <!ATTLIST tcp.mss
+ kind CDATA #FIXED "2"
+ length CDATA #FIXED "4"
+ size %Digits; #REQUIRED>
+
+7.3. UDPoXML DTD
+
+ <!--
+ DTD for UDP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ -->
+
+ <!ELEMENT udp (udp.pseudoheader?, udp.header, payload)>
+
+ <!ELEMENT udp.header (src, dest, udp.length, checksum)>
+
+ <!ELEMENT udp.pseudoheader (source, destination, protocol,
+ udp.length)>
+
+ <!--
+ udp header + data length in octets. does not include the size of
+ the pseudoheader.
+ -->
+ <!ELEMENT udp.length EMPTY>
+ <!ATTLIST udp.length
+ value %Digits; #REQUIRED>
+
+
+
+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:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ <ip>
+ <header length="474">
+ <version value="4"/>
+ <tos precedence="Routine" delay="Normal" throughput="Normal"
+ relibility="Normal" reserved="0"/>
+ <total.length value="461"/>
+ <id value="1"/>
+ <flags reserved="0" df="dont" mf="last"/>
+ <offset value="0"/>
+ <ttl value="255"/>
+ <protocol value="6"/>
+ <checksum value="8707"/>
+ <source address="10.0.0.22"/>
+ <destination address="10.0.0.1"/>
+ <options>
+ <end copied="0" class="0" number="0"/>
+ </options>
+ <padding pad="0"/>
+ </header>
+ <payload>
+ </payload>
+ </ip>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+3.2. Example Datagram
+
+ The following is an example TCPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ <tcp>
+ <tcp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <sequence number="322622954"/>
+ <acknowledgement number="689715995"/>
+ <offset number=""/>
+ <reserved value="0"/>
+ <control syn="1" ack="1"/>
+ <window size="1"/>
+ <urgent pointer="0"/>
+ <checksum value="2988"/>
+ <tcp.options>
+ <tcp.end kind="0"/>
+ </tcp.options>
+ <padding pad="0"/>
+ </tcp.header>
+ <payload>
+ </payload>
+ </tcp>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+4.2. Example Datagram
+
+ The following is an example UDPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ <udp>
+ <udp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <udp.length value="143"/>
+ <checksum value="2988"/>
+ </udp.header>
+ <payload>
+ </payload>
+ </udp>
+
+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 ("<?xml".)
+
+5.2. IEEE 802
+
+ BLOAT is encapsulated in IEEE 802 Networks as in [RFC1042] except
+ that the protocol type code for IPoXML is 0xBEEF.
+
+
+
+
+
+Kennedy Informational [Page 6]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+6. Gatewaying over IP
+
+ In order to facilitate the gradual introduction of BLOAT into the
+ public Internet, BLOAT MAY be encapsulated in IP as in [RFC2003] to
+ gateway between networks that run BLOAT natively on their LANs.
+
+7. DTDs
+
+ The Transport DTDs (7.2. and 7.3.) build on the definitions in the
+ Network DTD (7.1.)
+
+ The DTDs are referenced by their PubidLiteral and SystemLiteral (from
+ [XML]) although it is understood that most IPoXML implementations
+ will not need to pull down the DTD, as it will normally be embedded
+ in the implementation, and presents something of a catch-22 if you
+ need to load part of your network protocol over the network.
+
+7.1. IPoXML DTD
+
+ <!--
+ DTD for IP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ -->
+ <!--
+ DTD data types:
+
+ Digits [0..9]+
+
+ Precedence "NetworkControl | InternetworkControl |
+ CRITIC | FlashOverride | Flash | Immediate |
+ Priority | Routine"
+
+ IP4Addr "dotted-decimal" notation of [RFC1123]
+
+ Class [0..3]
+
+ Sec "Unclassified | Confidential | EFTO | MMMM | PROG |
+ Restricted | Secret | Top Secret | Reserved"
+
+ Compartments [0..65535]
+
+ Handling [0..65535]
+
+ TCC [0..16777216]
+
+ -->
+
+
+
+Kennedy Informational [Page 7]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ENTITY % Digits "CDATA">
+ <!ENTITY % Precedence "CDATA">
+ <!ENTITY % IP4Addr "CDATA">
+ <!ENTITY % Class "CDATA">
+ <!ENTITY % Sec "CDATA">
+ <!ENTITY % Compartments "CDATA">
+ <!ENTITY % Handling "CDATA">
+ <!ENTITY % TCC "CDATA">
+
+ <!ELEMENT ip (header, payload)>
+
+ <!ELEMENT header (version, tos, total.length, id, flags, offset, ttl,
+ protocol, checksum, source, destination, options,
+ padding)>
+ <!-- length of header in 32-bit words -->
+ <!ATTLIST header
+ length %Digits; #REQUIRED>
+
+ <!ELEMENT version EMPTY>
+ <!-- ip version. SHOULD be "4" -->
+ <!ATTLIST version
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT tos EMPTY>
+ <!ATTLIST tos
+ precedence %Precedence; #REQUIRED
+ delay (normal | low) #REQUIRED
+ throughput (normal | high) #REQUIRED
+ relibility (normal | high) #REQUIRED
+ reserved CDATA #FIXED "0">
+
+ <!ELEMENT total.length EMPTY>
+ <!--
+ total length of datagram (header and payload) in octets, MUST be
+ less than 65,535 (and SHOULD be less than 1024 for IPoXML on local
+ ethernets).
+ -->
+ <!ATTLIST total.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT id EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST id
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT flags EMPTY>
+ <!-- df = don't fragment, mf = more fragments -->
+ <!ATTLIST flags
+
+
+
+Kennedy Informational [Page 8]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ reserved CDATA #FIXED "0"
+ df (may|dont) #REQUIRED
+ mf (last|more) #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= offset <= 8192 measured in 8 octet (64-bit) chunks -->
+ <!ATTLIST offset
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT ttl EMPTY>
+ <!-- 0 <= ttl <= 255 -->
+ <!ATTLIST ttl
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT protocol EMPTY>
+ <!-- 0 <= protocol <= 255 (per IANA) -->
+ <!ATTLIST protocol
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT checksum EMPTY>
+ <!-- 0 <= checksum <= 65535 (over header only) -->
+ <!ATTLIST checksum
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT source EMPTY>
+ <!ATTLIST source
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT destination EMPTY>
+ <!ATTLIST destination
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT options ( end | noop | security | loose | strict | record
+ | stream | timestamp )*>
+
+ <!ELEMENT end EMPTY>
+ <!ATTLIST end
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "0">
+
+ <!ELEMENT noop EMPTY>
+ <!ATTLIST noop
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "1">
+
+ <!ELEMENT security EMPTY>
+
+
+
+Kennedy Informational [Page 9]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST security
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "2"
+ length CDATA #FIXED "11"
+ security %Sec; #REQUIRED
+ compartments %Compartments; #REQUIRED
+ handling %Handling; #REQUIRED
+ tcc %TCC; #REQUIRED>
+ <!ELEMENT loose (hop)+>
+ <!ATTLIST loose
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "3"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT hop EMPTY>
+ <!ATTLIST hop
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT strict (hop)+>
+ <!ATTLIST strict
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "9"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT record (hop)+>
+ <!ATTLIST record
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "7"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT stream EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST stream
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "8"
+ length CDATA #FIXED "4"
+ id %Digits; #REQUIRED>
+
+ <!ELEMENT timestamp (tstamp)+>
+ <!-- 0 <= oflw <=15 -->
+
+
+
+Kennedy Informational [Page 10]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST timestamp
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "2"
+ number CDATA #FIXED "4"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED
+ oflw %Digits; #REQUIRED
+ flag (0 | 1 | 3) #REQUIRED>
+
+ <!ELEMENT tstamp EMPTY>
+ <!ATTLIST tstamp
+ time %Digits; #REQUIRED
+ address %IP4Addr; #IMPLIED>
+ <!--
+ padding to bring header to 32-bit boundary.
+ pad MUST be "0"*
+ -->
+ <!ELEMENT padding EMPTY>
+ <!ATTLIST padding
+ pad CDATA #REQUIRED>
+
+ <!-- payload MUST be encoded as base-64 [RFC2045], as modified
+ by section 2.1 of this RFC -->
+ <!ELEMENT payload (CDATA)>
+
+7.2. TCPoXML DTD
+
+ <!--
+ DTD for TCP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ -->
+
+ <!-- the pseudoheader is only included for checksum calculations -->
+ <!ELEMENT tcp (tcp.pseudoheader?, tcp.header, payload)>
+
+ <!ELEMENT tcp.header (src, dest, sequence, acknowledgement, offset,
+ reserved, control, window, checksum, urgent,
+ tcp.options, padding)>
+
+ <!ELEMENT src EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+ <!ATTLIST src
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT dest EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+
+
+
+Kennedy Informational [Page 11]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST dest
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT sequence EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST sequence
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT acknowledgement EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST acknowledgement
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= number <= 255 -->
+ <!ATTLIST offset
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT reserved EMPTY>
+ <!ATTLIST reserved
+ value CDATA #FIXED "0">
+
+ <!ELEMENT control EMPTY>
+ <!ATTLIST control
+ urg (0|1) #IMPLIED
+ ack (0|1) #IMPLIED
+ psh (0|1) #IMPLIED
+ rst (0|1) #IMPLIED
+ syn (0|1) #IMPLIED
+ fin (0|1) #IMPLIED>
+
+ <!ELEMENT window EMPTY>
+ <!-- 0 <= size <= 65,535 -->
+ <!ATTLIST window
+ size %Digits; #REQUIRED>
+
+ <!--
+ checksum as in ip, but with
+ the following pseudo-header added into the tcp element:
+ -->
+ <!ELEMENT tcp.pseudoheader (source, destination, protocol,
+ tcp.length)>
+
+ <!--
+ tcp header + data length in octets. does not include the size of
+
+ the pseudoheader.
+ -->
+
+
+
+Kennedy Informational [Page 12]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ELEMENT tcp.length EMPTY>
+ <!ATTLIST tcp.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT urgent EMPTY>
+ <!-- 0 <= pointer <= 65,535 -->
+ <!ATTLIST urgent
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT tcp.options (tcp.end | tcp.noop | tcp.mss)+>
+
+ <!ELEMENT tcp.end EMPTY>
+ <!ATTLIST tcp.end
+ kind CDATA #FIXED "0">
+
+ <!ELEMENT tcp.noop EMPTY>
+ <!ATTLIST tcp.noop
+ kind CDATA #FIXED "1">
+
+ <!ELEMENT tcp.mss EMPTY>
+ <!ATTLIST tcp.mss
+ kind CDATA #FIXED "2"
+ length CDATA #FIXED "4"
+ size %Digits; #REQUIRED>
+
+7.3. UDPoXML DTD
+
+ <!--
+ DTD for UDP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ -->
+
+ <!ELEMENT udp (udp.pseudoheader?, udp.header, payload)>
+
+ <!ELEMENT udp.header (src, dest, udp.length, checksum)>
+
+ <!ELEMENT udp.pseudoheader (source, destination, protocol,
+ udp.length)>
+
+ <!--
+ udp header + data length in octets. does not include the size of
+ the pseudoheader.
+ -->
+ <!ELEMENT udp.length EMPTY>
+ <!ATTLIST udp.length
+ value %Digits; #REQUIRED>
+
+
+
+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:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ <ip>
+ <header length="474">
+ <version value="4"/>
+ <tos precedence="Routine" delay="Normal" throughput="Normal"
+ relibility="Normal" reserved="0"/>
+ <total.length value="461"/>
+ <id value="1"/>
+ <flags reserved="0" df="dont" mf="last"/>
+ <offset value="0"/>
+ <ttl value="255"/>
+ <protocol value="6"/>
+ <checksum value="8707"/>
+ <source address="10.0.0.22"/>
+ <destination address="10.0.0.1"/>
+ <options>
+ <end copied="0" class="0" number="0"/>
+ </options>
+ <padding pad="0"/>
+ </header>
+ <payload>
+ </payload>
+ </ip>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+3.2. Example Datagram
+
+ The following is an example TCPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ <tcp>
+ <tcp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <sequence number="322622954"/>
+ <acknowledgement number="689715995"/>
+ <offset number=""/>
+ <reserved value="0"/>
+ <control syn="1" ack="1"/>
+ <window size="1"/>
+ <urgent pointer="0"/>
+ <checksum value="2988"/>
+ <tcp.options>
+ <tcp.end kind="0"/>
+ </tcp.options>
+ <padding pad="0"/>
+ </tcp.header>
+ <payload>
+ </payload>
+ </tcp>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+4.2. Example Datagram
+
+ The following is an example UDPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ <udp>
+ <udp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <udp.length value="143"/>
+ <checksum value="2988"/>
+ </udp.header>
+ <payload>
+ </payload>
+ </udp>
+
+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 ("<?xml".)
+
+5.2. IEEE 802
+
+ BLOAT is encapsulated in IEEE 802 Networks as in [RFC1042] except
+ that the protocol type code for IPoXML is 0xBEEF.
+
+
+
+
+
+Kennedy Informational [Page 6]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+6. Gatewaying over IP
+
+ In order to facilitate the gradual introduction of BLOAT into the
+ public Internet, BLOAT MAY be encapsulated in IP as in [RFC2003] to
+ gateway between networks that run BLOAT natively on their LANs.
+
+7. DTDs
+
+ The Transport DTDs (7.2. and 7.3.) build on the definitions in the
+ Network DTD (7.1.)
+
+ The DTDs are referenced by their PubidLiteral and SystemLiteral (from
+ [XML]) although it is understood that most IPoXML implementations
+ will not need to pull down the DTD, as it will normally be embedded
+ in the implementation, and presents something of a catch-22 if you
+ need to load part of your network protocol over the network.
+
+7.1. IPoXML DTD
+
+ <!--
+ DTD for IP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ -->
+ <!--
+ DTD data types:
+
+ Digits [0..9]+
+
+ Precedence "NetworkControl | InternetworkControl |
+ CRITIC | FlashOverride | Flash | Immediate |
+ Priority | Routine"
+
+ IP4Addr "dotted-decimal" notation of [RFC1123]
+
+ Class [0..3]
+
+ Sec "Unclassified | Confidential | EFTO | MMMM | PROG |
+ Restricted | Secret | Top Secret | Reserved"
+
+ Compartments [0..65535]
+
+ Handling [0..65535]
+
+ TCC [0..16777216]
+
+ -->
+
+
+
+Kennedy Informational [Page 7]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ENTITY % Digits "CDATA">
+ <!ENTITY % Precedence "CDATA">
+ <!ENTITY % IP4Addr "CDATA">
+ <!ENTITY % Class "CDATA">
+ <!ENTITY % Sec "CDATA">
+ <!ENTITY % Compartments "CDATA">
+ <!ENTITY % Handling "CDATA">
+ <!ENTITY % TCC "CDATA">
+
+ <!ELEMENT ip (header, payload)>
+
+ <!ELEMENT header (version, tos, total.length, id, flags, offset, ttl,
+ protocol, checksum, source, destination, options,
+ padding)>
+ <!-- length of header in 32-bit words -->
+ <!ATTLIST header
+ length %Digits; #REQUIRED>
+
+ <!ELEMENT version EMPTY>
+ <!-- ip version. SHOULD be "4" -->
+ <!ATTLIST version
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT tos EMPTY>
+ <!ATTLIST tos
+ precedence %Precedence; #REQUIRED
+ delay (normal | low) #REQUIRED
+ throughput (normal | high) #REQUIRED
+ relibility (normal | high) #REQUIRED
+ reserved CDATA #FIXED "0">
+
+ <!ELEMENT total.length EMPTY>
+ <!--
+ total length of datagram (header and payload) in octets, MUST be
+ less than 65,535 (and SHOULD be less than 1024 for IPoXML on local
+ ethernets).
+ -->
+ <!ATTLIST total.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT id EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST id
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT flags EMPTY>
+ <!-- df = don't fragment, mf = more fragments -->
+ <!ATTLIST flags
+
+
+
+Kennedy Informational [Page 8]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ reserved CDATA #FIXED "0"
+ df (may|dont) #REQUIRED
+ mf (last|more) #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= offset <= 8192 measured in 8 octet (64-bit) chunks -->
+ <!ATTLIST offset
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT ttl EMPTY>
+ <!-- 0 <= ttl <= 255 -->
+ <!ATTLIST ttl
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT protocol EMPTY>
+ <!-- 0 <= protocol <= 255 (per IANA) -->
+ <!ATTLIST protocol
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT checksum EMPTY>
+ <!-- 0 <= checksum <= 65535 (over header only) -->
+ <!ATTLIST checksum
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT source EMPTY>
+ <!ATTLIST source
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT destination EMPTY>
+ <!ATTLIST destination
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT options ( end | noop | security | loose | strict | record
+ | stream | timestamp )*>
+
+ <!ELEMENT end EMPTY>
+ <!ATTLIST end
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "0">
+
+ <!ELEMENT noop EMPTY>
+ <!ATTLIST noop
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "1">
+
+ <!ELEMENT security EMPTY>
+
+
+
+Kennedy Informational [Page 9]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST security
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "2"
+ length CDATA #FIXED "11"
+ security %Sec; #REQUIRED
+ compartments %Compartments; #REQUIRED
+ handling %Handling; #REQUIRED
+ tcc %TCC; #REQUIRED>
+ <!ELEMENT loose (hop)+>
+ <!ATTLIST loose
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "3"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT hop EMPTY>
+ <!ATTLIST hop
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT strict (hop)+>
+ <!ATTLIST strict
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "9"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT record (hop)+>
+ <!ATTLIST record
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "7"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT stream EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST stream
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "8"
+ length CDATA #FIXED "4"
+ id %Digits; #REQUIRED>
+
+ <!ELEMENT timestamp (tstamp)+>
+ <!-- 0 <= oflw <=15 -->
+
+
+
+Kennedy Informational [Page 10]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST timestamp
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "2"
+ number CDATA #FIXED "4"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED
+ oflw %Digits; #REQUIRED
+ flag (0 | 1 | 3) #REQUIRED>
+
+ <!ELEMENT tstamp EMPTY>
+ <!ATTLIST tstamp
+ time %Digits; #REQUIRED
+ address %IP4Addr; #IMPLIED>
+ <!--
+ padding to bring header to 32-bit boundary.
+ pad MUST be "0"*
+ -->
+ <!ELEMENT padding EMPTY>
+ <!ATTLIST padding
+ pad CDATA #REQUIRED>
+
+ <!-- payload MUST be encoded as base-64 [RFC2045], as modified
+ by section 2.1 of this RFC -->
+ <!ELEMENT payload (CDATA)>
+
+7.2. TCPoXML DTD
+
+ <!--
+ DTD for TCP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ -->
+
+ <!-- the pseudoheader is only included for checksum calculations -->
+ <!ELEMENT tcp (tcp.pseudoheader?, tcp.header, payload)>
+
+ <!ELEMENT tcp.header (src, dest, sequence, acknowledgement, offset,
+ reserved, control, window, checksum, urgent,
+ tcp.options, padding)>
+
+ <!ELEMENT src EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+ <!ATTLIST src
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT dest EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+
+
+
+Kennedy Informational [Page 11]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST dest
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT sequence EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST sequence
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT acknowledgement EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST acknowledgement
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= number <= 255 -->
+ <!ATTLIST offset
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT reserved EMPTY>
+ <!ATTLIST reserved
+ value CDATA #FIXED "0">
+
+ <!ELEMENT control EMPTY>
+ <!ATTLIST control
+ urg (0|1) #IMPLIED
+ ack (0|1) #IMPLIED
+ psh (0|1) #IMPLIED
+ rst (0|1) #IMPLIED
+ syn (0|1) #IMPLIED
+ fin (0|1) #IMPLIED>
+
+ <!ELEMENT window EMPTY>
+ <!-- 0 <= size <= 65,535 -->
+ <!ATTLIST window
+ size %Digits; #REQUIRED>
+
+ <!--
+ checksum as in ip, but with
+ the following pseudo-header added into the tcp element:
+ -->
+ <!ELEMENT tcp.pseudoheader (source, destination, protocol,
+ tcp.length)>
+
+ <!--
+ tcp header + data length in octets. does not include the size of
+
+ the pseudoheader.
+ -->
+
+
+
+Kennedy Informational [Page 12]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ELEMENT tcp.length EMPTY>
+ <!ATTLIST tcp.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT urgent EMPTY>
+ <!-- 0 <= pointer <= 65,535 -->
+ <!ATTLIST urgent
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT tcp.options (tcp.end | tcp.noop | tcp.mss)+>
+
+ <!ELEMENT tcp.end EMPTY>
+ <!ATTLIST tcp.end
+ kind CDATA #FIXED "0">
+
+ <!ELEMENT tcp.noop EMPTY>
+ <!ATTLIST tcp.noop
+ kind CDATA #FIXED "1">
+
+ <!ELEMENT tcp.mss EMPTY>
+ <!ATTLIST tcp.mss
+ kind CDATA #FIXED "2"
+ length CDATA #FIXED "4"
+ size %Digits; #REQUIRED>
+
+7.3. UDPoXML DTD
+
+ <!--
+ DTD for UDP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ -->
+
+ <!ELEMENT udp (udp.pseudoheader?, udp.header, payload)>
+
+ <!ELEMENT udp.header (src, dest, udp.length, checksum)>
+
+ <!ELEMENT udp.pseudoheader (source, destination, protocol,
+ udp.length)>
+
+ <!--
+ udp header + data length in octets. does not include the size of
+ the pseudoheader.
+ -->
+ <!ELEMENT udp.length EMPTY>
+ <!ATTLIST udp.length
+ value %Digits; #REQUIRED>
+
+
+
+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 <QtCore>
+
+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
--- /dev/null
+++ b/tests/auto/network/access/qnetworkreply/empty
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
--- /dev/null
+++ b/tests/auto/network/access/qnetworkreply/image1.jpg
Binary files 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
--- /dev/null
+++ b/tests/auto/network/access/qnetworkreply/image2.jpg
Binary files 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
--- /dev/null
+++ b/tests/auto/network/access/qnetworkreply/image3.jpg
Binary files 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 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+ <file>resource</file>
+</qresource>
+</RCC>
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:[<mediatype>][;base64],<data>
+
+ The <mediatype> 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 <mediatype> 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 <A> 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:
+
+ <IMG
+ SRC="data:image/gif;base64,R0lGODdhMAAwAPAAAAAAAP///ywAAAAAMAAw
+ AAAC8IyPqcvt3wCcDkiLc7C0qwyGHhSWpjQu5yqmCYsapyuvUUlvONmOZtfzgFz
+ ByTB10QgxOR0TqBQejhRNzOfkVJ+5YiUqrXF5Y5lKh/DeuNcP5yLWGsEbtLiOSp
+ a/TPg7JpJHxyendzWTBfX0cxOnKPjgBzi4diinWGdkF8kjdfnycQZXZeYGejmJl
+ ZeGl9i2icVqaNVailT6F5iJ90m6mvuTS4OK05M0vDk0Q4XUtwvKOzrcd3iq9uis
+ F81M1OIcR7lEewwcLp7tuNNkM3uNna3F2JQFo97Vriy/Xl4/f1cf5VWzXyym7PH
+ hhx4dbgYKAAA7"
+ ALT="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:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ <ip>
+ <header length="474">
+ <version value="4"/>
+ <tos precedence="Routine" delay="Normal" throughput="Normal"
+ relibility="Normal" reserved="0"/>
+ <total.length value="461"/>
+ <id value="1"/>
+ <flags reserved="0" df="dont" mf="last"/>
+ <offset value="0"/>
+ <ttl value="255"/>
+ <protocol value="6"/>
+ <checksum value="8707"/>
+ <source address="10.0.0.22"/>
+ <destination address="10.0.0.1"/>
+ <options>
+ <end copied="0" class="0" number="0"/>
+ </options>
+ <padding pad="0"/>
+ </header>
+ <payload>
+ </payload>
+ </ip>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+3.2. Example Datagram
+
+ The following is an example TCPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ <tcp>
+ <tcp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <sequence number="322622954"/>
+ <acknowledgement number="689715995"/>
+ <offset number=""/>
+ <reserved value="0"/>
+ <control syn="1" ack="1"/>
+ <window size="1"/>
+ <urgent pointer="0"/>
+ <checksum value="2988"/>
+ <tcp.options>
+ <tcp.end kind="0"/>
+ </tcp.options>
+ <padding pad="0"/>
+ </tcp.header>
+ <payload>
+ </payload>
+ </tcp>
+
+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 <?xml?> header
+ as well as the <!DOCTYPE> declaration.
+
+4.2. Example Datagram
+
+ The following is an example UDPoXML datagram with an empty payload:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ <udp>
+ <udp.header>
+ <src port="31415"/>
+ <dest port="42424"/>
+ <udp.length value="143"/>
+ <checksum value="2988"/>
+ </udp.header>
+ <payload>
+ </payload>
+ </udp>
+
+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 ("<?xml".)
+
+5.2. IEEE 802
+
+ BLOAT is encapsulated in IEEE 802 Networks as in [RFC1042] except
+ that the protocol type code for IPoXML is 0xBEEF.
+
+
+
+
+
+Kennedy Informational [Page 6]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+6. Gatewaying over IP
+
+ In order to facilitate the gradual introduction of BLOAT into the
+ public Internet, BLOAT MAY be encapsulated in IP as in [RFC2003] to
+ gateway between networks that run BLOAT natively on their LANs.
+
+7. DTDs
+
+ The Transport DTDs (7.2. and 7.3.) build on the definitions in the
+ Network DTD (7.1.)
+
+ The DTDs are referenced by their PubidLiteral and SystemLiteral (from
+ [XML]) although it is understood that most IPoXML implementations
+ will not need to pull down the DTD, as it will normally be embedded
+ in the implementation, and presents something of a catch-22 if you
+ need to load part of your network protocol over the network.
+
+7.1. IPoXML DTD
+
+ <!--
+ DTD for IP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
+ -->
+ <!--
+ DTD data types:
+
+ Digits [0..9]+
+
+ Precedence "NetworkControl | InternetworkControl |
+ CRITIC | FlashOverride | Flash | Immediate |
+ Priority | Routine"
+
+ IP4Addr "dotted-decimal" notation of [RFC1123]
+
+ Class [0..3]
+
+ Sec "Unclassified | Confidential | EFTO | MMMM | PROG |
+ Restricted | Secret | Top Secret | Reserved"
+
+ Compartments [0..65535]
+
+ Handling [0..65535]
+
+ TCC [0..16777216]
+
+ -->
+
+
+
+Kennedy Informational [Page 7]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ENTITY % Digits "CDATA">
+ <!ENTITY % Precedence "CDATA">
+ <!ENTITY % IP4Addr "CDATA">
+ <!ENTITY % Class "CDATA">
+ <!ENTITY % Sec "CDATA">
+ <!ENTITY % Compartments "CDATA">
+ <!ENTITY % Handling "CDATA">
+ <!ENTITY % TCC "CDATA">
+
+ <!ELEMENT ip (header, payload)>
+
+ <!ELEMENT header (version, tos, total.length, id, flags, offset, ttl,
+ protocol, checksum, source, destination, options,
+ padding)>
+ <!-- length of header in 32-bit words -->
+ <!ATTLIST header
+ length %Digits; #REQUIRED>
+
+ <!ELEMENT version EMPTY>
+ <!-- ip version. SHOULD be "4" -->
+ <!ATTLIST version
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT tos EMPTY>
+ <!ATTLIST tos
+ precedence %Precedence; #REQUIRED
+ delay (normal | low) #REQUIRED
+ throughput (normal | high) #REQUIRED
+ relibility (normal | high) #REQUIRED
+ reserved CDATA #FIXED "0">
+
+ <!ELEMENT total.length EMPTY>
+ <!--
+ total length of datagram (header and payload) in octets, MUST be
+ less than 65,535 (and SHOULD be less than 1024 for IPoXML on local
+ ethernets).
+ -->
+ <!ATTLIST total.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT id EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST id
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT flags EMPTY>
+ <!-- df = don't fragment, mf = more fragments -->
+ <!ATTLIST flags
+
+
+
+Kennedy Informational [Page 8]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ reserved CDATA #FIXED "0"
+ df (may|dont) #REQUIRED
+ mf (last|more) #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= offset <= 8192 measured in 8 octet (64-bit) chunks -->
+ <!ATTLIST offset
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT ttl EMPTY>
+ <!-- 0 <= ttl <= 255 -->
+ <!ATTLIST ttl
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT protocol EMPTY>
+ <!-- 0 <= protocol <= 255 (per IANA) -->
+ <!ATTLIST protocol
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT checksum EMPTY>
+ <!-- 0 <= checksum <= 65535 (over header only) -->
+ <!ATTLIST checksum
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT source EMPTY>
+ <!ATTLIST source
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT destination EMPTY>
+ <!ATTLIST destination
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT options ( end | noop | security | loose | strict | record
+ | stream | timestamp )*>
+
+ <!ELEMENT end EMPTY>
+ <!ATTLIST end
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "0">
+
+ <!ELEMENT noop EMPTY>
+ <!ATTLIST noop
+ copied (0|1) #REQUIRED
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "1">
+
+ <!ELEMENT security EMPTY>
+
+
+
+Kennedy Informational [Page 9]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST security
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "2"
+ length CDATA #FIXED "11"
+ security %Sec; #REQUIRED
+ compartments %Compartments; #REQUIRED
+ handling %Handling; #REQUIRED
+ tcc %TCC; #REQUIRED>
+ <!ELEMENT loose (hop)+>
+ <!ATTLIST loose
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "3"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT hop EMPTY>
+ <!ATTLIST hop
+ address %IP4Addr; #REQUIRED>
+
+ <!ELEMENT strict (hop)+>
+ <!ATTLIST strict
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "9"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT record (hop)+>
+ <!ATTLIST record
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "7"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT stream EMPTY>
+ <!-- 0 <= id <= 65,535 -->
+ <!ATTLIST stream
+ copied CDATA #FIXED "1"
+ class CDATA #FIXED "0"
+ number CDATA #FIXED "8"
+ length CDATA #FIXED "4"
+ id %Digits; #REQUIRED>
+
+ <!ELEMENT timestamp (tstamp)+>
+ <!-- 0 <= oflw <=15 -->
+
+
+
+Kennedy Informational [Page 10]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST timestamp
+ copied CDATA #FIXED "0"
+ class CDATA #FIXED "2"
+ number CDATA #FIXED "4"
+ length %Digits; #REQUIRED
+ pointer %Digits; #REQUIRED
+ oflw %Digits; #REQUIRED
+ flag (0 | 1 | 3) #REQUIRED>
+
+ <!ELEMENT tstamp EMPTY>
+ <!ATTLIST tstamp
+ time %Digits; #REQUIRED
+ address %IP4Addr; #IMPLIED>
+ <!--
+ padding to bring header to 32-bit boundary.
+ pad MUST be "0"*
+ -->
+ <!ELEMENT padding EMPTY>
+ <!ATTLIST padding
+ pad CDATA #REQUIRED>
+
+ <!-- payload MUST be encoded as base-64 [RFC2045], as modified
+ by section 2.1 of this RFC -->
+ <!ELEMENT payload (CDATA)>
+
+7.2. TCPoXML DTD
+
+ <!--
+ DTD for TCP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
+ -->
+
+ <!-- the pseudoheader is only included for checksum calculations -->
+ <!ELEMENT tcp (tcp.pseudoheader?, tcp.header, payload)>
+
+ <!ELEMENT tcp.header (src, dest, sequence, acknowledgement, offset,
+ reserved, control, window, checksum, urgent,
+ tcp.options, padding)>
+
+ <!ELEMENT src EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+ <!ATTLIST src
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT dest EMPTY>
+ <!-- 0 <= port <= 65,535 -->
+
+
+
+Kennedy Informational [Page 11]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ATTLIST dest
+ port %Digits; #REQUIRED>
+
+ <!ELEMENT sequence EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST sequence
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT acknowledgement EMPTY>
+ <!-- 0 <= number <= 4294967295 -->
+ <!ATTLIST acknowledgement
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT offset EMPTY>
+ <!-- 0 <= number <= 255 -->
+ <!ATTLIST offset
+ number %Digits; #REQUIRED>
+
+ <!ELEMENT reserved EMPTY>
+ <!ATTLIST reserved
+ value CDATA #FIXED "0">
+
+ <!ELEMENT control EMPTY>
+ <!ATTLIST control
+ urg (0|1) #IMPLIED
+ ack (0|1) #IMPLIED
+ psh (0|1) #IMPLIED
+ rst (0|1) #IMPLIED
+ syn (0|1) #IMPLIED
+ fin (0|1) #IMPLIED>
+
+ <!ELEMENT window EMPTY>
+ <!-- 0 <= size <= 65,535 -->
+ <!ATTLIST window
+ size %Digits; #REQUIRED>
+
+ <!--
+ checksum as in ip, but with
+ the following pseudo-header added into the tcp element:
+ -->
+ <!ELEMENT tcp.pseudoheader (source, destination, protocol,
+ tcp.length)>
+
+ <!--
+ tcp header + data length in octets. does not include the size of
+
+ the pseudoheader.
+ -->
+
+
+
+Kennedy Informational [Page 12]
+
+RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
+
+
+ <!ELEMENT tcp.length EMPTY>
+ <!ATTLIST tcp.length
+ value %Digits; #REQUIRED>
+
+ <!ELEMENT urgent EMPTY>
+ <!-- 0 <= pointer <= 65,535 -->
+ <!ATTLIST urgent
+ pointer %Digits; #REQUIRED>
+
+ <!ELEMENT tcp.options (tcp.end | tcp.noop | tcp.mss)+>
+
+ <!ELEMENT tcp.end EMPTY>
+ <!ATTLIST tcp.end
+ kind CDATA #FIXED "0">
+
+ <!ELEMENT tcp.noop EMPTY>
+ <!ATTLIST tcp.noop
+ kind CDATA #FIXED "1">
+
+ <!ELEMENT tcp.mss EMPTY>
+ <!ATTLIST tcp.mss
+ kind CDATA #FIXED "2"
+ length CDATA #FIXED "4"
+ size %Digits; #REQUIRED>
+
+7.3. UDPoXML DTD
+
+ <!--
+ DTD for UDP over XML.
+ Refer to this DTD as:
+
+ <!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
+ -->
+
+ <!ELEMENT udp (udp.pseudoheader?, udp.header, payload)>
+
+ <!ELEMENT udp.header (src, dest, udp.length, checksum)>
+
+ <!ELEMENT udp.pseudoheader (source, destination, protocol,
+ udp.length)>
+
+ <!--
+ udp header + data length in octets. does not include the size of
+ the pseudoheader.
+ -->
+ <!ELEMENT udp.length EMPTY>
+ <!ATTLIST udp.length
+ value %Digits; #REQUIRED>
+
+
+
+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 <QtTest/QtTest>
+#include <QtCore/QCryptographicHash>
+#include <QtCore/QDataStream>
+#include <QtCore/QUrl>
+#include <QtCore/QEventLoop>
+#include <QtCore/QFile>
+#include <QtCore/QSharedPointer>
+#include <QtCore/QScopedPointer>
+#include <QtCore/QTemporaryFile>
+#include <QtNetwork/QTcpServer>
+#include <QtNetwork/QTcpSocket>
+#include <QtNetwork/QLocalSocket>
+#include <QtNetwork/QLocalServer>
+#include <QtNetwork/QHostInfo>
+#include <QtNetwork/QFtp>
+#include <QtNetwork/QAbstractNetworkCache>
+#include <QtNetwork/qauthenticator.h>
+#include <QtNetwork/qnetworkaccessmanager.h>
+#include <QtNetwork/qnetworkrequest.h>
+#include <QtNetwork/qnetworkreply.h>
+#include <QtNetwork/qnetworkcookie.h>
+#include <QtNetwork/QHttpPart>
+#include <QtNetwork/QHttpMultiPart>
+#ifndef QT_NO_OPENSSL
+#include <QtNetwork/qsslerror.h>
+#include <QtNetwork/qsslconfiguration.h>
+#endif
+#ifndef QT_NO_BEARERMANAGEMENT
+#include <QtNetwork/qnetworkconfigmanager.h>
+#include <QtNetwork/qnetworkconfiguration.h>
+#include <QtNetwork/qnetworksession.h>
+#endif
+
+#include <time.h>
+
+#include "private/qnetworkaccessmanager_p.h"
+
+#ifdef Q_OS_SYMBIAN
+#define SRCDIR "."
+#endif
+
+#include "../../../network-settings.h"
+
+Q_DECLARE_METATYPE(QSharedPointer<char>)
+Q_DECLARE_METATYPE(QNetworkReply*)
+Q_DECLARE_METATYPE(QAuthenticator*)
+Q_DECLARE_METATYPE(QNetworkProxy)
+Q_DECLARE_METATYPE(QNetworkProxyQuery)
+Q_DECLARE_METATYPE(QList<QNetworkProxy>)
+Q_DECLARE_METATYPE(QNetworkReply::NetworkError)
+Q_DECLARE_METATYPE(QBuffer*)
+Q_DECLARE_METATYPE(QHttpMultiPart *)
+Q_DECLARE_METATYPE(QList<QFile*>) // for multiparts
+#ifndef QT_NO_OPENSSL
+Q_DECLARE_METATYPE(QSslConfiguration)
+#endif
+
+class QNetworkReplyPtr: public QSharedPointer<QNetworkReply>
+{
+public:
+ inline QNetworkReplyPtr(QNetworkReply *ptr = 0)
+ : QSharedPointer<QNetworkReply>(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<ProxyData> proxies;
+ QNetworkAccessManager manager;
+ MyCookieJar *cookieJar;
+#ifndef QT_NO_OPENSSL
+ QSslConfiguration storedSslConfiguration;
+ QList<QSslError> storedExpectedSslErrors;
+#endif
+#ifndef QT_NO_BEARERMANAGEMENT
+ QNetworkConfigurationManager *netConfMan;
+ QNetworkConfiguration networkConfiguration;
+ QScopedPointer<QNetworkSession> 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<QSslError> &);
+ void storeSslConfiguration();
+ void ignoreSslErrorListSlot(QNetworkReply *reply, const QList<QSslError> &);
+#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<QNetworkCookie> &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<QSslError>)), this, SLOT(slotSslErrors(QList<QSslError>)));
+ 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<QSslError>& 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<QNetworkCookie> allCookies() const
+ { return QNetworkCookieJar::allCookies(); }
+ inline void setAllCookies(const QList<QNetworkCookie> &cookieList)
+ { QNetworkCookieJar::setAllCookies(cookieList); }
+};
+
+class MyProxyFactory: public QNetworkProxyFactory
+{
+public:
+ int callCount;
+ QList<QNetworkProxy> toReturn;
+ QNetworkProxyQuery lastQuery;
+ inline MyProxyFactory() { clear(); }
+
+ inline void clear()
+ {
+ callCount = 0;
+ toReturn = QList<QNetworkProxy>() << QNetworkProxy::DefaultProxy;
+ lastQuery = QNetworkProxyQuery();
+ }
+
+ virtual QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery &query)
+ {
+ lastQuery = query;
+ ++callCount;
+ return toReturn;
+ }
+};
+
+class MyMemoryCache: public QAbstractNetworkCache
+{
+public:
+ typedef QPair<QNetworkCacheMetaData, QByteArray> CachedContent;
+ typedef QHash<QByteArray, CachedContent> 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<QUrl, QIODevice*> m_buffers;
+ QList<QUrl> 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<QSslError>)), this, SLOT(slotSslErrors(QList<QSslError>)));
+ setupSslServer(serverSocket);
+ serverSocket->startServerEncryption();
+ sslSocket = serverSocket;
+ } else
+#endif
+ {
+ QTcpServer::incomingConnection(socketDescriptor);
+ }
+ }
+private slots:
+
+#ifndef QT_NO_OPENSSL
+ void slotSslErrors(const QList<QSslError>& 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<int>(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 "<readBufferSize> 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<QNetworkReply *>(); // for QSignalSpy
+ qRegisterMetaType<QAuthenticator *>();
+ qRegisterMetaType<QNetworkProxy>();
+#ifndef QT_NO_OPENSSL
+ qRegisterMetaType<QList<QSslError> >();
+#endif
+ qRegisterMetaType<QNetworkReply::NetworkError>();
+
+ 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<QSslError> &errors)
+{
+ reply->ignoreSslErrors();
+ QVERIFY(!errors.isEmpty());
+ QVERIFY(!reply->sslConfiguration().isNull());
+}
+
+void tst_QNetworkReply::storeSslConfiguration()
+{
+ storedSslConfiguration = QSslConfiguration();
+ QNetworkReply *reply = qobject_cast<QNetworkReply *>(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<QNetworkCookie>());
+}
+
+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<QString>("request");
+ QTest::addColumn<QByteArray>("expected");
+ QTest::addColumn<QString>("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("<body contentEditable=true>\r\n") << defaultMimeType;
+ QTest::newRow("pct_2") << "data:text/html;charset=utf-8,%3Cbody%20contentEditable%3Dtrue%3E%0D%0A"
+ << QByteArray("<body contentEditable=true>\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("<e/>")
+ << "application/xml";
+
+ QTest::newRow("base64, no media type")
+ << QString::fromLatin1("data:;base64,PGUvPg==")
+ << QByteArray("<e/>")
+ << defaultMimeType;
+
+ QTest::newRow("Percent encoding")
+ << QString::fromLatin1("data:application/xml,%3Ce%2F%3E")
+ << QByteArray("<e/>")
+ << "application/xml";
+
+ QTest::newRow("Percent encoding, no media type")
+ << QString::fromLatin1("data:,%3Ce%2F%3E")
+ << QByteArray("<e/>")
+ << 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<QString>("fileName");
+ QTest::addColumn<QString>("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<QString>("referenceName");
+ QTest::addColumn<QString>("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<QString>("referenceName");
+ QTest::addColumn<QString>("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<QString>("url");
+ QTest::addColumn<int>("error");
+ QTest::addColumn<int>("httpStatusCode");
+ QTest::addColumn<bool>("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<QByteArray>("data");
+ QTest::addColumn<QByteArray>("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<QUrl>("url");
+ QTest::addColumn<QHttpMultiPart *>("multiPart");
+ QTest::addColumn<QByteArray>("expectedReplyData");
+ QTest::addColumn<QByteArray>("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<QByteArray> 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<QByteArray> 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<QUrl>("url");
+ QTest::addColumn<int>("resultCode");
+ QTest::addColumn<QNetworkReply::NetworkError>("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<QUrl>("putUrl");
+ QTest::addColumn<int>("putResultCode");
+ QTest::addColumn<QNetworkReply::NetworkError>("putError");
+ QTest::addColumn<QUrl>("deleteUrl");
+ QTest::addColumn<int>("deleteResultCode");
+ QTest::addColumn<QNetworkReply::NetworkError>("deleteError");
+ QTest::addColumn<QUrl>("get2Url");
+ QTest::addColumn<int>("get2ResultCode");
+ QTest::addColumn<QNetworkReply::NetworkError>("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<QUrl>("url");
+ QTest::addColumn<QNetworkReply::NetworkError>("error");
+ QTest::addColumn<QByteArray>("dataToSend");
+ QTest::addColumn<QByteArray>("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<QUrl>("url");
+ QTest::addColumn<QByteArray>("verb");
+ QTest::addColumn<QBuffer *>("device");
+ QTest::addColumn<int>("resultCode");
+ QTest::addColumn<QNetworkReply::NetworkError>("error");
+ QTest::addColumn<QByteArray>("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<QString>("urlStr");
+ QTest::addColumn<QByteArray>("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("<body contentEditable=true>\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<QString>("fileName");
+ QTest::addColumn<qint64>("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<QUrl>("url");
+ QTest::addColumn<QByteArray>("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<QNetworkProxy>(); // for QSignalSpy
+ qRegisterMetaType<QAuthenticator *>();
+
+ // 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<QNetworkProxy>(); // for QSignalSpy
+ qRegisterMetaType<QAuthenticator *>();
+
+ 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<QNetworkReply*>(); // for QSignalSpy
+ qRegisterMetaType<QList<QSslError> >();
+
+ 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<QSslError>)));
+ connect(&manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
+ SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
+ 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<QSslError>)),
+ this, SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
+
+ 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<QNetworkReply*>(); // for QSignalSpy
+ qRegisterMetaType<QList<QSslError> >();
+
+ 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<QSslError>)));
+ 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<QNetworkReply*>(); // for QSignalSpy
+ qRegisterMetaType<QList<QSslError> >();
+
+ 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<QSslError>)));
+ 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<QByteArray>("dataToSend");
+ QTest::addColumn<bool>("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<QByteArray>("dataToSend");
+ QTest::addColumn<int>("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<QByteArray>("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<MyMemoryCache::CachedContent>();
+ QTest::addColumn<QByteArray>("dataToSend");
+ QTest::addColumn<QString>("body");
+ QTest::addColumn<MyMemoryCache::CachedContent>("cachedReply");
+ QTest::addColumn<int>("cacheMode");
+ QTest::addColumn<QStringList>("extraHttpHeaders");
+ QTest::addColumn<bool>("loadedFromCache");
+ QTest::addColumn<bool>("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<QList<QNetworkProxy> >("proxyList");
+ QTest::addColumn<QNetworkProxy>("proxyUsed");
+ QTest::addColumn<QString>("url");
+ QTest::addColumn<QNetworkReply::NetworkError>("expectedError");
+
+ QList<QNetworkProxy> 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<QNetworkProxy>(); // for QSignalSpy
+ qRegisterMetaType<QAuthenticator *>();
+
+ QFile reference(SRCDIR "/rfc3252.txt");
+ QVERIFY(reference.open(QIODevice::ReadOnly));
+
+ // set the proxy factory:
+ QFETCH(QList<QNetworkProxy>, 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<QSslError>)),
+ SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
+#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<QSslError>)),
+ this, SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
+#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<QNetworkProxy>(authspy.at(0).at(0)), proxyUsed);
+ }
+ } else {
+ // request failed
+ QCOMPARE(authspy.count(), 0);
+ }
+}
+
+void tst_QNetworkReply::ioPutToFileFromFile_data()
+{
+ QTest::addColumn<QString>("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<QByteArray>("data");
+ QTest::addColumn<QByteArray>("md5sum");
+ QTest::addColumn<QUrl>("url");
+ QTest::addColumn<QNetworkProxy>("proxy");
+ QTest::addColumn<int>("authenticationRequiredCount");
+ QTest::addColumn<int>("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<QNetworkProxy>(); // for QSignalSpy
+ qRegisterMetaType<QAuthenticator *>();
+ qRegisterMetaType<QNetworkReply *>();
+
+ 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<QByteArray>("data");
+ QTest::addColumn<QByteArray>("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<QSslError>&)), 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<QSslSocket*>(sender())->bytesAvailable() << static_cast<QSslSocket*>(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<QSslError>&)), 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<QVariant> 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<QVariant> 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<bool>("https");
+ QTest::addColumn<int>("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<QVariant> 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<QVariant> 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<QVariant> 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<int>("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>("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<int>("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<QVariant> 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<QVariant> 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<QVariant> 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<QString>("cookieString");
+ QTest::addColumn<QList<QNetworkCookie> >("expectedCookiesFromHttp");
+ QTest::addColumn<QList<QNetworkCookie> >("expectedCookiesInJar");
+
+ QTest::newRow("empty") << "" << QList<QNetworkCookie>() << QList<QNetworkCookie>();
+
+ QList<QNetworkCookie> 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<QNetworkCookie> setCookies =
+ qvariant_cast<QList<QNetworkCookie> >(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<QNetworkCookie> setCookies =
+ qvariant_cast<QList<QNetworkCookie> >(reply->header(QNetworkRequest::SetCookieHeader));
+ QTEST(setCookies, "expectedCookiesFromHttp");
+ QTEST(cookieJar->allCookies(), "expectedCookiesInJar");
+}
+
+void tst_QNetworkReply::sendCookies_data()
+{
+ QTest::addColumn<QList<QNetworkCookie> >("cookiesToSet");
+ QTest::addColumn<QString>("expectedCookieString");
+
+ QList<QNetworkCookie> 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<QNetworkCookie>, 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<QNetworkCookie>, 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<QNetworkReply::NetworkError>();
+
+ 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<QUrl>("url");
+ QTest::addColumn<QByteArray>("responseToSend");
+ QTest::addColumn<QString>("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<QThread, QThreadCleanup> serverThread(new QThread);
+ QScopedPointer<MiniHttpServer, QDeleteLaterCleanup> 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<QString>("url");
+ QTest::addColumn<int>("errorSignalCount");
+ QTest::addColumn<int>("finishedSignalCount");
+ QTest::addColumn<int>("error");
+ QTest::addColumn<int>("httpStatusCode");
+ QTest::addColumn<QString>("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>("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<bool>("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<QNetworkReply*>(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<QNetworkReply*>(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<QString>("url");
+ QTest::addColumn<QList<QSslError> >("expectedSslErrors");
+ QTest::addColumn<QNetworkReply::NetworkError>("expectedNetworkError");
+
+ QList<QSslError> expectedSslErrors;
+ // apparently, because of some weird behaviour of SRCDIR, the file name below needs to start with a slash
+ QList<QSslCertificate> 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<QSslError>, 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<QSslError> &)
+{
+ 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<QSslError>, expectedSslErrors);
+ // store the errors to ignore them later in the slot connected below
+ storedExpectedSslErrors = expectedSslErrors;
+ connect(&manager, SIGNAL(sslErrors(QNetworkReply *, const QList<QSslError> &)),
+ this, SLOT(ignoreSslErrorListSlot(QNetworkReply *, const QList<QSslError> &)));
+
+
+ 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<QSslConfiguration>("configuration");
+ QTest::addColumn<bool>("works");
+
+ QTest::newRow("empty") << QSslConfiguration() << false;
+ QSslConfiguration conf = QSslConfiguration::defaultConfiguration();
+ QTest::newRow("default") << conf << false; // does not contain test server cert
+ QList<QSslCertificate> 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<bool>("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<QUrl>("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<char> sharedPointer = downloadBufferAttribute.value<QSharedPointer<char> >();
+ bool memoryComparison =
+ (0 == memcmp(static_cast<void*>(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<qint64> 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<char> sharedPointer = qvariant_cast<QSharedPointer<char> >(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<bool>("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*>("QNetworkReply*");
+ qRegisterMetaType<QAuthenticator*>("QAuthenticator*");
+ QSignalSpy authSpy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
+ QSignalSpy finishedSpy(&manager, SIGNAL(finished(QNetworkReply*)));
+ qRegisterMetaType<QNetworkReply::NetworkError>("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<int>(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*>("QNetworkReply*");
+ qRegisterMetaType<QAuthenticator*>("QAuthenticator*");
+ QSignalSpy authSpy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
+ QSignalSpy finishedSpy(&manager, SIGNAL(finished(QNetworkReply*)));
+ qRegisterMetaType<QNetworkReply::NetworkError>("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<QUrl>("url");
+ QTest::addColumn<QString>("expected");
+ QTest::addColumn<bool>("checkContentLength");
+ QTest::addColumn<QString>("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<QSslCertificate> 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<QSslError>)));
+ 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<QSslError> &)));
+ 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 <QtTest/QtTest>
+#include <QtCore/QUrl>
+#include <QtNetwork/QNetworkRequest>
+#include <QtNetwork/QNetworkCookie>
+
+Q_DECLARE_METATYPE(QList<QByteArray>)
+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<QNetworkCookie> &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<QUrl>("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<QByteArray>("header");
+ QTest::addColumn<QByteArray>("value");
+ QTest::addColumn<QByteArray>("headerToGet");
+ QTest::addColumn<QByteArray>("expectedValue");
+ QTest::addColumn<bool>("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<QList<QByteArray> >("set");
+ QTest::addColumn<QList<QByteArray> >("expected");
+
+ QTest::newRow("empty") << QList<QByteArray>() << QList<QByteArray>();
+
+ QList<QByteArray> set;
+ QList<QByteArray> 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<QByteArray>, set);
+ QFETCH(QList<QByteArray>, expected);
+
+ QNetworkRequest request;
+ foreach (QByteArray header, set)
+ request.setRawHeader(header, "a value");
+
+ QList<QByteArray> 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<QNetworkRequest::KnownHeaders>("cookedHeader");
+ QTest::addColumn<QVariant>("cookedValue");
+ QTest::addColumn<bool>("success");
+ QTest::addColumn<QString>("rawHeader");
+ QTest::addColumn<QString>("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<QNetworkCookie>() << cookie)
+ << true << "Cookie"
+ << "a=b";
+ QTest::newRow("SetCookie-1") << QNetworkRequest::SetCookieHeader
+ << qVariantFromValue(QList<QNetworkCookie>() << cookie)
+ << true << "Set-Cookie"
+ << "a=b";
+
+ cookie.setPath("/");
+ QTest::newRow("Cookie-2") << QNetworkRequest::CookieHeader
+ << qVariantFromValue(QList<QNetworkCookie>() << cookie)
+ << true << "Cookie"
+ << "a=b";
+ QTest::newRow("SetCookie-2") << QNetworkRequest::SetCookieHeader
+ << qVariantFromValue(QList<QNetworkCookie>() << cookie)
+ << true << "Set-Cookie"
+ << "a=b; path=/";
+
+ QNetworkCookie cookie2;
+ cookie2.setName("c");
+ cookie2.setValue("d");
+ QTest::newRow("Cookie-3") << QNetworkRequest::CookieHeader
+ << qVariantFromValue(QList<QNetworkCookie>() << cookie << cookie2)
+ << true << "Cookie"
+ << "a=b; c=d";
+ QTest::newRow("SetCookie-3") << QNetworkRequest::SetCookieHeader
+ << qVariantFromValue(QList<QNetworkCookie>() << 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<QNetworkRequest::KnownHeaders>("cookedHeader");
+ QTest::addColumn<QVariant>("cookedValue");
+ QTest::addColumn<bool>("success");
+ QTest::addColumn<QString>("rawHeader");
+ QTest::addColumn<QString>("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<QNetworkCookie>() << cookie)
+ << true << "Cookie"
+ << "a=b";
+ QTest::newRow("SetCookie-1") << QNetworkRequest::SetCookieHeader
+ << qVariantFromValue(QList<QNetworkCookie>() << cookie)
+ << true << "Set-Cookie"
+ << "a=b";
+
+ cookie.setPath("/");
+ QTest::newRow("SetCookie-2") << QNetworkRequest::SetCookieHeader
+ << qVariantFromValue(QList<QNetworkCookie>() << 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<QNetworkCookie>() << cookie << cookie2)
+ << true << "Cookie"
+ << "a=b; c=d";
+ cookie.setPath("/");
+ QTest::newRow("SetCookie-3") << QNetworkRequest::SetCookieHeader
+ << qVariantFromValue(QList<QNetworkCookie>() << 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<QList<QNetworkCookie> >())
+ QCOMPARE(qvariant_cast<QList<QNetworkCookie> >(request.header(cookedHeader)),
+ qvariant_cast<QList<QNetworkCookie> >(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 <QtTest/QtTest>
+#include "../qbearertestcommon.h"
+
+#include <QtNetwork/qnetworkconfiguration.h>
+#include <QtNetwork/qnetworkconfigmanager.h>
+
+/*
+ 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 <QNetworkAccessManager>
+
+#if defined(Q_OS_UNIX) && !defined(QT_NO_ICD) && !defined (Q_OS_SYMBIAN)
+#include <stdio.h>
+#include <iapconf.h>
+#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; i<MAX_IAPS; i++) {
+ QString num = QString().sprintf("%d", i);
+ QString iap = "iap-" + num;
+ iaps[i] = new Maemo::IAPConf(iap);
+ iaps[i]->setValue("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; i<MAX_IAPS; i++) {
+ iaps[i]->clear();
+ 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<QNetworkConfiguration> preScanConfigs = manager.allConfigurations();
+
+ QSignalSpy spy(&manager, SIGNAL(updateCompleted()));
+ manager.updateConfigurations(); //initiate scans
+ QTRY_VERIFY(spy.count() == 1); //wait for scan to complete
+
+ QList<QNetworkConfiguration> 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<QNetworkConfiguration> 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<QNetworkConfiguration> 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<QNetworkConfiguration> 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 <QtTest/QtTest>
+#include "../qbearertestcommon.h"
+
+#include <QtNetwork/qnetworkconfiguration.h>
+#include <QtNetwork/qnetworkconfigmanager.h>
+
+#if defined(Q_OS_UNIX) && !defined(QT_NO_ICD) && !defined (Q_OS_SYMBIAN)
+#include <stdio.h>
+#include <iapconf.h>
+#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; i<MAX_IAPS; i++) {
+ QString num = QString().sprintf("%d", i);
+ QString iap = "iap-" + num;
+ iaps[i] = new Maemo::IAPConf(iap);
+ iaps[i]->setValue("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; i<MAX_IAPS; i++) {
+ iaps[i]->clear();
+ 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.isValid() << " type->"<< p.type() <<
+ " roaming->" << p.isRoamingAvailable() << "identifier->" << p.identifier() <<
+ " purpose->" << p.purpose() << " state->" << p.state();
+}
+
+void tst_QNetworkConfigurationManager::allConfigurations()
+{
+ QNetworkConfigurationManager manager;
+ QList<QNetworkConfiguration> 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<QNetworkConfiguration> 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<QNetworkConfiguration> 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<QString> 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<QNetworkConfiguration> 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<QNetworkConfiguration> configs;
+ QList<QNetworkConfiguration> 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<QNetworkConfiguration> preScanConfigs = manager.allConfigurations();
+ QSignalSpy spy(&manager, SIGNAL(updateCompleted()));
+ manager.updateConfigurations(); //initiate scans
+ QTRY_VERIFY(spy.count() == 1); //wait for scan to complete
+ QList<QNetworkConfiguration> 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 <QCoreApplication>
+#include <QStringList>
+#include <QLocalSocket>
+
+#include <QtNetwork/qnetworkconfiguration.h>
+#include <QtNetwork/qnetworkconfigmanager.h>
+#include <QtNetwork/qnetworksession.h>
+
+#include <QEventLoop>
+#include <QTimer>
+#include <QDebug>
+
+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<QNetworkConfiguration> 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 <QtTest/QtTest>
+#include <QLocalServer>
+#include <QLocalSocket>
+#include <QTimer>
+#include "../../qbearertestcommon.h"
+
+#include <QtNetwork/qnetworkconfigmanager.h>
+#include <QtNetwork/qnetworksession.h>
+
+#if defined(Q_OS_UNIX) && !defined(QT_NO_ICD) && !defined (Q_OS_SYMBIAN)
+#include <stdio.h>
+#include <iapconf.h>
+#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<QString, bool> 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>("QNetworkConfiguration");
+ qRegisterMetaType<QNetworkConfiguration::Type>("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; i<MAX_IAPS; i++) {
+ QString num = QString().sprintf("%d", i);
+ QString iap = "iap-" + num;
+ iaps[i] = new Maemo::IAPConf(iap);
+ iaps[i]->setValue("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; i<MAX_IAPS; i++) {
+ iaps[i]->clear();
+ 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<QString>("bearerType");
+ QTest::addColumn<QNetworkConfiguration::Type>("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<QNetworkSession::SessionError> (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<QNetworkSession::SessionError> (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<QNetworkConfiguration>("configuration");
+
+ QTest::newRow("invalid configuration") << QNetworkConfiguration();
+
+ foreach (const QNetworkConfiguration &config, manager.allConfigurations()) {
+ const QString name = config.name().isEmpty() ? QString("<Hidden>") : 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<QString>("bearerType");
+ QTest::addColumn<QNetworkConfiguration::Type>("configurationType");
+ QTest::addColumn<int>("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<QNetworkSession::SessionError>(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<QString>("bearerType");
+ QTest::addColumn<QNetworkConfiguration::Type>("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<QNetworkSession::SessionError>(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<QNetworkSession::SessionError>(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<QNetworkConfiguration>("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<QNetworkSession::SessionError>(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<QNetworkConfiguration>("configuration");
+ QTest::addColumn<bool>("forceSessionStop");
+
+ foreach (const QNetworkConfiguration &config, manager.allConfigurations()) {
+ const QString name = config.name().isEmpty() ? QString("<Hidden>") : 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<QNetworkSession::SessionError>(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<QNetworkSession::State>(stateChangedSpy.at(0).at(0));
+ QVERIFY(state == QNetworkSession::Connecting);
+
+ state = qvariant_cast<QNetworkSession::State>(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<QNetworkSession::SessionError>(errorSpy.first().at(0));
+ QNetworkSession::SessionError error2 =
+ qvariant_cast<QNetworkSession::SessionError>(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<QNetworkSession::SessionError>(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<QNetworkSession::State>(stateChangedSpy2.at(0).at(0));
+ QVERIFY(state == QNetworkSession::Connecting);
+
+ state = qvariant_cast<QNetworkSession::State>(stateChangedSpy2.at(1).at(0));
+ QVERIFY(state == QNetworkSession::Connected);
+
+ state = qvariant_cast<QNetworkSession::State>(stateChangedSpy2.at(2).at(0));
+ QVERIFY(state == QNetworkSession::Closing);
+
+ state = qvariant_cast<QNetworkSession::State>(stateChangedSpy2.at(3).at(0));
+ QVERIFY(state == QNetworkSession::Disconnected);
+ } else if (stateChangedSpy2.count() == 2) {
+ state = qvariant_cast<QNetworkSession::State>(stateChangedSpy2.at(0).at(0));
+ QVERIFY(state == QNetworkSession::Closing);
+
+ state = qvariant_cast<QNetworkSession::State>(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<QNetworkSession::State>(stateChangedSpy.at(stateChangedSpy.count() - 1).at(0));
+
+ for (int i = 0; i < stateChangedSpy.count(); i++) {
+ QNetworkSession::State state_temp =
+ qvariant_cast<QNetworkSession::State>(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<QNetworkSession::State>(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<QNetworkSession::State>(stateChangedSpy2.at(0).at(0));
+ QVERIFY(state == QNetworkSession::Closing);
+ state = qvariant_cast<QNetworkSession::State>(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<QNetworkSession::State>(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<QNetworkSession::State>(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<QNetworkSession::SessionError>(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<QNetworkConfiguration> &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<QNetworkConfiguration> 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<QNetworkConfiguration>(spy.takeFirst().at(0));
+ } while (changed.identifier() != identifier);
+
+ QVERIFY((changed.state() & QNetworkConfiguration::Active) ==
+ QNetworkConfiguration::Active);
+
+ QVERIFY(!before.contains(changed));
+
+ QList<QNetworkConfiguration> 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<QNetworkConfiguration>(spy.takeFirst().at(0));
+ } while (changed.identifier() != identifier);
+
+ QVERIFY((changed.state() & QNetworkConfiguration::Active) !=
+ QNetworkConfiguration::Active);
+
+ QList<QNetworkConfiguration> 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<QNetworkConfiguration> 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<QNetworkConfiguration> 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<QNetworkConfiguration>("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("<Hidden>") : 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 <QtCore/QString>
+#include <QtTest/QtTest>
+#include <QtCore/QCoreApplication>
+#include <QtNetwork/QAuthenticator>
+
+#include <private/qauthenticator_p.h>
+
+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<QString>("data");
+ QTest::addColumn<QString>("realm");
+ QTest::addColumn<QString>("user");
+ QTest::addColumn<QString>("password");
+ QTest::addColumn<QByteArray>("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<QPair<QByteArray, QByteArray> > 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<QString>("data");
+ QTest::addColumn<QString>("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<QPair<QByteArray, QByteArray> > 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<QByteArray, QByteArray>("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 <qcoreapplication.h>
+#include <QtTest/QtTest>
+#include <qhostaddress.h>
+#include <qplatformdefs.h>
+#include <qdebug.h>
+#include <qhash.h>
+#include <qbytearray.h>
+#include <qdatastream.h>
+
+//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("<invalid>");
+ 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>("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<QString>("address");
+ QTest::addColumn<bool>("ok");
+ QTest::addColumn<QString>("resAddr");
+ QTest::addColumn<int>("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<QString>("text");
+ QTest::addColumn<int>("address");
+ QTest::addColumn<bool>("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<QHostAddress>("first");
+ QTest::addColumn<QHostAddress>("second");
+ QTest::addColumn<bool>("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<QHostAddress, QString> hostHash;
+ hostHash.insert(QHostAddress(), "ole");
+}
+
+void tst_QHostAddress::streaming_data()
+{
+ QTest::addColumn<QHostAddress>("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<QString>("subnet");
+ QTest::addColumn<QHostAddress>("prefix");
+ QTest::addColumn<int>("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<QHostAddress, int> result = QHostAddress::parseSubnet(subnet);
+ QCOMPARE(result.first, prefix);
+ QCOMPARE(result.second, prefixLength);
+}
+
+void tst_QHostAddress::isInSubnet_data()
+{
+ QTest::addColumn<QHostAddress>("address");
+ QTest::addColumn<QHostAddress>("prefix");
+ QTest::addColumn<int>("prefixLength");
+ QTest::addColumn<bool>("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 <qglobal.h>
+
+// 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 <winsock2.h>
+# include <ws2tcpip.h>
+#endif
+
+#include <QtTest/QtTest>
+#include <qcoreapplication.h>
+#include <QDebug>
+#include <QTcpSocket>
+#include <private/qthread_p.h>
+#include <QTcpServer>
+
+#ifndef QT_NO_BEARERMANAGEMENT
+#include <QtNetwork/qnetworkconfigmanager.h>
+#include <QtNetwork/qnetworkconfiguration.h>
+#include <QtNetwork/qnetworksession.h>
+#endif
+
+#include <time.h>
+#include <qlibrary.h>
+#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
+#include <windows.h>
+#else
+#include <unistd.h>
+#include <signal.h>
+#endif
+
+#include <qhostinfo.h>
+#include "private/qhostinfo_p.h"
+
+#if !defined(QT_NO_GETADDRINFO)
+# if !defined(Q_OS_WINCE)
+# include <sys/types.h>
+# else
+# include <types.h>
+# endif
+# if defined(Q_OS_UNIX)
+# include <sys/socket.h>
+# endif
+# if !defined(Q_OS_WIN)
+# include <netdb.h>
+# 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<QNetworkSession> 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<bool>("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<QString>("hostname");
+ QTest::addColumn<QString>("addresses");
+ QTest::addColumn<int>("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<QString>("hostname");
+ QTest::addColumn<QString>("addresses");
+ QTest::addColumn<int>("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<QString>("address");
+ QTest::addColumn<QStringList>("hostNames");
+ QTest::addColumn<int>("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;i<numrequests;i++)
+ QHostInfo::lookupHost(QString("qt.nokia.com"), this, SLOT(resultsReady(const QHostInfo&)));
+}
+
+void LookupReceiver::resultsReady(const QHostInfo &info)
+{
+ result = info;
+ numrequests--;
+ if (numrequests == 0 || info.error() != QHostInfo::NoError)
+ QThread::currentThread()->quit();
+}
+
+void tst_QHostInfo::threadSafetyAsynchronousAPI()
+{
+ const int nattempts = 10;
+ const int lookupsperthread = 10;
+ QList<QThread*> threads;
+ QList<LookupReceiver*> 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<int>("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 <QtTest/QtTest>
+#include <qcoreapplication.h>
+#include <qnetworkinterface.h>
+
+// 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<QHostAddress>("ip");
+ QTest::addColumn<QHostAddress>("netmask");
+ QTest::addColumn<int>("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 <QtTest/QtTest>
+
+#include <qcoreapplication.h>
+#include <qnetworkinterface.h>
+#include <qtcpsocket.h>
+#include <QNetworkConfigurationManager>
+#include <QNetworkSession>
+#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<QNetworkSession> 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<QNetworkInterface> 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<QHostAddress> all = QNetworkInterface::allAddresses();
+ QVERIFY(all.contains(QHostAddress(QHostAddress::LocalHost)));
+}
+
+void tst_QNetworkInterface::loopbackIPv6()
+{
+ QList<QHostAddress> 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<QHostAddress> all = QNetworkInterface::allAddresses();
+ QVERIFY(all.contains(local));
+}
+
+void tst_QNetworkInterface::interfaceFromXXX()
+{
+ QList<QNetworkInterface> 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 <QtTest/QtTest>
+
+#include <qcoreapplication.h>
+#include <qdebug.h>
+#include <qnetworkproxy.h>
+
+//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 <QtTest/QTest>
+#include <QtTest/QTestEventLoop>
+
+#include <qcoreapplication.h>
+#include <qdebug.h>
+#include <qnetworkproxy.h>
+
+#include <QNetworkConfiguration>
+#include <QNetworkConfigurationManager>
+#include <QNetworkSession>
+#include <QNetworkAccessManager>
+#include <QNetworkReply>
+#include <QNetworkRequest>
+#include <QList>
+
+Q_DECLARE_METATYPE(QNetworkConfiguration);
+Q_DECLARE_METATYPE(QList<QNetworkProxy>);
+
+#include <QThread>
+
+class tst_QNetworkProxyFactory : public QObject {
+ Q_OBJECT
+
+public:
+ tst_QNetworkProxyFactory();
+
+ class QDebugProxyFactory : public QNetworkProxyFactory
+ {
+ public:
+ virtual QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery &query = QNetworkProxyQuery())
+ {
+ returnedList = QNetworkProxyFactory::systemProxyForQuery(query);
+ requestCounter++;
+ return returnedList;
+ }
+ QList<QNetworkProxy> 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<QNetworkProxy> systemProxyList = QNetworkProxyFactory::systemProxyForQuery(query);
+ bool pass = true;
+ QNetworkProxy proxy;
+
+ QList<QNetworkProxy> 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<QNetworkProxy> 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<QNetworkConfiguration>("config");
+ QTest::addColumn<QList<QNetworkProxy> >("proxies");
+ QNetworkConfigurationManager manager;
+ //get from known configurations
+ foreach (QNetworkConfiguration config, manager.allConfigurations()) {
+ QNetworkProxyQuery query(config, QUrl(QString("http://qt.nokia.com")), QNetworkProxyQuery::UrlRequest);
+ QList<QNetworkProxy> 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<QNetworkProxy>, 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() << " <vs> ";
+ 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<QNetworkProxy> 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 <QtTest/QTest>
+
+#ifdef Q_OS_WIN
+#include <winsock2.h>
+#endif
+
+#include <qcoreapplication.h>
+
+
+#include <qdatastream.h>
+
+#include <qhostaddress.h>
+#include <qdatetime.h>
+
+#ifdef Q_OS_UNIX
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#endif
+
+#include <stddef.h>
+
+#ifdef Q_OS_SYMBIAN
+#include <QNetworkConfigurationManager>
+#include <QNetworkConfiguration>
+#include <QNetworkSession>
+#include <QScopedPointer>
+#define PLATFORMSOCKETENGINE QSymbianSocketEngine
+#define PLATFORMSOCKETENGINESTRING "QSymbianSocketEngine"
+#include <private/qsymbiansocketengine_p.h>
+#include <private/qcore_symbian_p.h>
+#else
+#define PLATFORMSOCKETENGINE QNativeSocketEngine
+#define PLATFORMSOCKETENGINESTRING "QNativeSocketEngine"
+#include <private/qnativesocketengine_p.h>
+#endif
+
+#include <qstringlist.h>
+
+#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<QNetworkConfigurationManager> netConfMan(new QNetworkConfigurationManager());
+ QNetworkConfiguration networkConfiguration(netConfMan->defaultConfiguration());
+ QScopedPointer<QNetworkSession> 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<PLATFORMSOCKETENGINE *> 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 <QtTest/QtTest>
+
+#include <qcoreapplication.h>
+#include <qdebug.h>
+#include <qabstractsocket.h>
+
+//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 <QtTest/QTest>
+#include <QtTest/QTestEventLoop>
+#include <QtCore/QQueue>
+#include <QtCore/QString>
+#include <QtCore/QCoreApplication>
+
+#include <private/qhttpsocketengine_p.h>
+#include <qhostinfo.h>
+#include <qhostaddress.h>
+#include <qtcpsocket.h>
+#include <qhttp.h>
+#include <qdebug.h>
+#include <qtcpserver.h>
+
+#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<QByteArray> dataToTransmit;
+
+public:
+ QByteArray receivedData;
+
+ MiniHttpServer(const QList<QByteArray> &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<QString>("hostname");
+ QTest::addColumn<int>("port");
+ QTest::addColumn<QString>("username");
+ QTest::addColumn<QString>("response");
+ QTest::addColumn<int>("expectedError");
+
+ QQueue<QByteArray> 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 <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <string.h>
+#include <qstring.h>
+#include <qdebug.h>
+
+#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 <qcoreapplication.h>
+#include <qdebug.h>
+
+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 <qscriptengine.h>
+ #include <QFile>
+#include <QTest>
+
+#include <qlocalsocket.h>
+#include <qlocalserver.h>
+
+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 <typename T>
+static QScriptValue _q_ScriptValueFromQObject(QScriptEngine *engine, T* const &in)
+{
+ return engine->newQObject(in);
+}
+template <typename T>
+static void _q_ScriptValueToQObject(const QScriptValue &v, T* &out)
+{ out = qobject_cast<T*>(v.toQObject());
+}
+template <typename T>
+static int _q_ScriptRegisterQObjectMetaType(QScriptEngine *engine, const QScriptValue &prototype)
+{
+ return qScriptRegisterMetaType<T*>(engine, _q_ScriptValueFromQObject<T>, _q_ScriptValueToQObject<T>, 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<QScriptLocalServer>(&eng, QScriptValue());
+
+ QScriptValue lss = qScriptValueFromQMetaObject<QScriptLocalServer>(&eng);
+ eng.globalObject().setProperty("QScriptLocalServer", lss);
+
+ _q_ScriptRegisterQObjectMetaType<QScriptLocalSocket>(&eng, QScriptValue());
+
+ QScriptValue lsc = qScriptValueFromQMetaObject<QScriptLocalSocket>(&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 <QtTest/QtTest>
+
+#include <qtextstream.h>
+#include <QtNetwork/qlocalsocket.h>
+#include <QtNetwork/qlocalserver.h>
+#include "../../../../shared/util.h"
+
+#ifdef Q_OS_SYMBIAN
+ #include <unistd.h>
+#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>("QLocalSocket::LocalSocketState");
+ qRegisterMetaType<QLocalSocket::LocalSocketError>("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<int> 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<QString>("name");
+ QTest::addColumn<bool>("canListen");
+ QTest::addColumn<bool>("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<QString>("name");
+ QTest::addColumn<bool>("canListen");
+ QTest::addColumn<int>("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<QLocalSocket*> 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<QLocalSocket::LocalSocketError>(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<QLocalSocket::LocalSocketError>(spyError.first()[0]),
+ QLocalSocket::SocketTimeoutError);
+ } else {
+ QCOMPARE(qVariantValue<QLocalSocket::LocalSocketError>(spyError.first()[0]),
+ QLocalSocket::ServerNotFoundError);
+ }
+
+ // Check first and last state
+ QCOMPARE(qVariantValue<QLocalSocket::LocalSocketState>(spyStateChanged.first()[0]),
+ QLocalSocket::ConnectingState);
+#if 0
+ for (int j = 0; j < spyStateChanged.count(); ++j) {
+ QLocalSocket::LocalSocketState s;
+ s = qVariantValue<QLocalSocket::LocalSocketState>(spyStateChanged.at(j).at(0));
+ qDebug() << s;
+ }
+#endif
+ if (canListen)
+ QCOMPARE(qVariantValue<QLocalSocket::LocalSocketState>(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<int>("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<QLocalSocket*> 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<int>("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<Client*> 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<int>("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<QProcess*> 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 <QtTest/QTest>
+#include <QtTest/QTestEventLoop>
+
+#include <QtCore/QQueue>
+#include <QtCore/QString>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QMetaType>
+
+#include <private/qsocks5socketengine_p.h>
+#include <qhostinfo.h>
+#include <qhostaddress.h>
+#include <qtcpsocket.h>
+#include <qhttp.h>
+#include <qauthenticator.h>
+#include <qdebug.h>
+#include <qtcpserver.h>
+#include <qmetatype.h>
+#include <qdebug.h>
+
+#include "../../../network-settings.h"
+
+Q_DECLARE_METATYPE(QQueue<QByteArray>)
+
+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<QByteArray> responses;
+
+ MiniSocks5Server(const QQueue<QByteArray> 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<QTcpSocket *>(sender());
+ QQueue<QByteArray> pendingResponses =
+ qvariant_cast<QQueue<QByteArray> >(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<QString>("hostname");
+ QTest::addColumn<int>("port");
+ QTest::addColumn<QString>("username");
+ QTest::addColumn<QQueue<QByteArray> >("responses");
+ QTest::addColumn<int>("expectedError");
+
+ QQueue<QByteArray> 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<QByteArray>, 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 <QtCore>
+#include <QtNetwork>
+
+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 <qglobal.h>
+#if defined(_WIN32) && !defined(Q_OS_SYMBIAN)
+#include <winsock2.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#define SOCKET int
+#define INVALID_SOCKET -1
+#endif
+
+#include <QtTest/QtTest>
+
+#ifndef Q_OS_WIN
+#include <unistd.h>
+#include <sys/ioctl.h>
+#endif
+
+#include <qcoreapplication.h>
+#include <qtcpsocket.h>
+#include <qtcpserver.h>
+#include <qhostaddress.h>
+#include <qprocess.h>
+#include <qstringlist.h>
+#include <qplatformdefs.h>
+#include <qhostinfo.h>
+
+#include <QNetworkProxy>
+Q_DECLARE_METATYPE(QNetworkProxy)
+Q_DECLARE_METATYPE(QList<QNetworkProxy>)
+
+#include <QNetworkSession>
+#include <QNetworkConfiguration>
+#include <QNetworkConfigurationManager>
+#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<bool>("setProxy");
+ QTest::addColumn<int>("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<QHostAddress>("bindAddress");
+ QTest::addColumn<bool>("v4ok");
+ QTest::addColumn<bool>("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<int>("type");
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<int>("port");
+ QTest::addColumn<int>("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<QNetworkProxy> toReturn;
+ QNetworkProxyQuery lastQuery;
+ inline MyProxyFactory() { clear(); }
+
+ inline void clear()
+ {
+ callCount = 0;
+ toReturn = QList<QNetworkProxy>() << QNetworkProxy::DefaultProxy;
+ lastQuery = QNetworkProxyQuery();
+ }
+
+ virtual QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery &query)
+ {
+ lastQuery = query;
+ ++callCount;
+ return toReturn;
+ }
+};
+
+void tst_QTcpServer::proxyFactory_data()
+{
+ QTest::addColumn<QList<QNetworkProxy> >("proxyList");
+ QTest::addColumn<QNetworkProxy>("proxyUsed");
+ QTest::addColumn<bool>("fails");
+ QTest::addColumn<int>("expectedError");
+
+ QList<QNetworkProxy> 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<QNetworkProxy>, 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<QTcpServer*>(sender());
+ QTcpSocket* s=server->nextPendingConnection();
+ connect(s,SIGNAL(readyRead()),this,SLOT(onServerReadyRead()));
+ }
+ void onServerReadyRead() {
+ QTcpSocket* clientSocket=static_cast<QTcpSocket*>(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 <QByteArray>
+#include <QCoreApplication>
+#include <QDataStream>
+#include <QTimer>
+
+// 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 <QTcpServer>
+#include <QTcpSocket>
+
+//------------------------------------------------------------------------------
+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 <QCoreApplication>
+#include <QStringList>
+
+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 <qt4client|qt4server>");
+ 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 <qglobal.h>
+
+#if defined(_WIN32) && !defined(Q_OS_SYMBIAN)
+#include <winsock2.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <unistd.h>
+#define SOCKET int
+#define INVALID_SOCKET -1
+#endif
+
+#include <qplatformdefs.h>
+
+#include <QtTest/QtTest>
+
+#include <QAuthenticator>
+#include <QCoreApplication>
+#include <QEventLoop>
+#include <QFile>
+#include <QHostAddress>
+#include <QHostInfo>
+#include <QMap>
+#ifndef Q_OS_VXWORKS
+#include <QMessageBox>
+#include <QPushButton>
+#endif
+#include <QPointer>
+#include <QProcess>
+#include <QStringList>
+#include <QTcpServer>
+#include <QTcpSocket>
+#ifndef QT_NO_OPENSSL
+#include <QSslSocket>
+#endif
+#include <QTextStream>
+#include <QThread>
+#include <QTime>
+#include <QTimer>
+#include <QDebug>
+// RVCT compiles also unused inline methods
+# include <QNetworkProxy>
+
+#ifdef Q_OS_LINUX
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#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<QNetworkProxy>)
+
+//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<bool>("setProxy");
+ QTest::addColumn<int>("proxyType");
+ QTest::addColumn<bool>("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<QHostAddress> 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<QString>("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<QByteArray>("data");
+ QTest::addColumn<bool>("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<ReceiverThread*>(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<ReceiverThread, ReceiverThread> 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<bool>("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>("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<QTcpSocket *>(sender())->close();
+}
+
+void tst_QTcpSocket::messageBoxSlot()
+{
+#if !defined(Q_OS_VXWORKS) // no gui
+ QTcpSocket *socket = qobject_cast<QTcpSocket *>(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<QTcpSocket> 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>("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<const int *>(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>("QAbstractSocket::SocketError");
+ qRegisterMetaType<QAbstractSocket::SocketState>("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<QAbstractSocket::SocketState>(stateSpy.at(0).at(0)), QAbstractSocket::HostLookupState);
+ QCOMPARE(qVariantValue<QAbstractSocket::SocketState>(stateSpy.at(1).at(0)), QAbstractSocket::ConnectingState);
+ QCOMPARE(qVariantValue<QAbstractSocket::SocketState>(stateSpy.at(2).at(0)), QAbstractSocket::UnconnectedState);
+ QCOMPARE(errorSpy.count(), 1);
+
+ delete socket;
+}
+
+//----------------------------------------------------------------------------------
+void tst_QTcpSocket::suddenRemoteDisconnect_data()
+{
+ QTest::addColumn<QString>("client");
+ QTest::addColumn<QString>("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<int>("type");
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<int>("port");
+ QTest::addColumn<bool>("failsAtConnect");
+ QTest::addColumn<int>("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<QNetworkProxy> toReturn;
+ QNetworkProxyQuery lastQuery;
+ inline MyProxyFactory() { clear(); }
+
+ inline void clear()
+ {
+ callCount = 0;
+ toReturn = QList<QNetworkProxy>() << QNetworkProxy::DefaultProxy;
+ lastQuery = QNetworkProxyQuery();
+ }
+
+ virtual QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery &query)
+ {
+ lastQuery = query;
+ ++callCount;
+ return toReturn;
+ }
+};
+
+void tst_QTcpSocket::proxyFactory_data()
+{
+ QTest::addColumn<QList<QNetworkProxy> >("proxyList");
+ QTest::addColumn<QNetworkProxy>("proxyUsed");
+ QTest::addColumn<bool>("failsAtConnect");
+ QTest::addColumn<int>("expectedError");
+
+ QList<QNetworkProxy> 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<QNetworkProxy>, 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 <QtNetwork>
+
+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 <server> <port>|UnconnectedClient <server> <port>|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 <QtTest/QtTest>
+
+#include <qcoreapplication.h>
+#include <qfileinfo.h>
+#include <qdatastream.h>
+#include <qudpsocket.h>
+#include <qhostaddress.h>
+#include <qhostinfo.h>
+#include <qmap.h>
+#include <QNetworkProxy>
+#include <QNetworkInterface>
+
+#include <qstringlist.h>
+#include "../../../network-settings.h"
+
+#ifndef QT_NO_BEARERMANAGEMENT
+#include <QtNetwork/qnetworkconfigmanager.h>
+#include <QtNetwork/qnetworkconfiguration.h>
+#include <QtNetwork/qnetworksession.h>
+#endif
+
+Q_DECLARE_METATYPE(QHostAddress)
+Q_DECLARE_METATYPE(QNetworkInterface)
+Q_DECLARE_METATYPE(QSharedPointer<QNetworkSession>)
+//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<QNetworkSession> networkSession;
+#endif
+};
+
+tst_QUdpSocket::tst_QUdpSocket()
+{
+ Q_SET_DEFAULT_IAP
+}
+
+tst_QUdpSocket::~tst_QUdpSocket()
+{
+}
+
+void tst_QUdpSocket::initTestCase_data()
+{
+ QTest::addColumn<bool>("setProxy");
+ QTest::addColumn<int>("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<QNetworkSession>(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>("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<QHostAddress> broadcastAddresses;
+ foreach (QNetworkInterface iface, QNetworkInterface::allInterfaces()) {
+ if ((iface.flags() & QNetworkInterface::CanBroadcast)
+ && iface.flags() & QNetworkInterface::IsUp) {
+ for (int i=0;i<iface.addressEntries().count();i++)
+ broadcastAddresses.append(iface.addressEntries().at(i).broadcast());
+ }
+ }
+ if (broadcastAddresses.isEmpty())
+ QSKIP("No interface can broadcast", SkipAll);
+ for (int i = 0; i < 4; ++i) {
+ QUdpSocket serverSocket;
+#ifdef FORCE_SESSION
+ serverSocket.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+ QVERIFY2(serverSocket.bind(QHostAddress::Any, 5000), serverSocket.errorString().toLatin1().constData());
+
+ QCOMPARE(serverSocket.state(), QUdpSocket::BoundState);
+
+ connect(&serverSocket, SIGNAL(readyRead()), SLOT(empty_readyReadSlot()));
+
+ QUdpSocket broadcastSocket;
+#ifdef FORCE_SESSION
+ broadcastSocket.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+ broadcastSocket.bind();
+
+ for (int j = 0; j < 100; ++j) {
+ for (int k = 0; k < 4; k++) {
+ broadcastSocket.writeDatagram(message[i], strlen(message[i]),
+ QHostAddress::Broadcast, 5000);
+ foreach (QHostAddress addr, broadcastAddresses)
+ broadcastSocket.writeDatagram(message[i], strlen(message[i]), addr, 5000);
+ }
+ QTestEventLoop::instance().enterLoop(15);
+ if (QTestEventLoop::instance().timeout()) {
+#if defined(Q_OS_FREEBSD)
+ QEXPECT_FAIL("",
+ "Broadcasting to 255.255.255.255 does not work on FreeBSD",
+ Abort);
+ QVERIFY(false); // seems that QFAIL() doesn't respect the QEXPECT_FAIL() :/
+#endif
+ QFAIL("Network operation timed out");
+ }
+ QVERIFY(serverSocket.hasPendingDatagrams());
+
+ do {
+ QByteArray arr; arr.resize(serverSocket.pendingDatagramSize() + 1);
+ QHostAddress host;
+ quint16 port;
+ QCOMPARE((int) serverSocket.readDatagram(arr.data(), arr.size() - 1, &host, &port),
+ (int) strlen(message[i]));
+ arr.resize(strlen(message[i]));
+ QCOMPARE(arr, QByteArray(message[i]));
+ } while (serverSocket.hasPendingDatagrams());
+ }
+ }
+}
+
+//----------------------------------------------------------------------------------
+
+void tst_QUdpSocket::loop_data()
+{
+ QTest::addColumn<QByteArray>("peterMessage");
+ QTest::addColumn<QByteArray>("paulMessage");
+ QTest::addColumn<bool>("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>("qint64");
+ qRegisterMetaType<QAbstractSocket::SocketError>("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<const int *>(errorspy.at(0).at(0).constData()),
+ int(QUdpSocket::DatagramTooLargeError));
+ QCOMPARE(client.error(), QUdpSocket::DatagramTooLargeError);
+ break;
+ }
+ QVERIFY(bytesspy.count() == 1);
+ QCOMPARE(*static_cast<const qint64 *>(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<bool>("bind");
+ QTest::addColumn<QHostAddress>("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<QHostAddress>("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>("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<QHostAddress>("bindAddress");
+ QTest::addColumn<int>("ttl");
+ QTest::addColumn<int>("expected");
+
+ QList<QHostAddress> 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<QHostAddress>("bindAddress");
+ QTest::addColumn<int>("loopback");
+ QTest::addColumn<int>("expected");
+
+ QList<QHostAddress> 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<QHostAddress>("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<QHostAddress>("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<QNetworkInterface>("iface");
+ QTest::addColumn<QHostAddress>("address");
+ QList<QNetworkInterface> 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<QHostAddress>("bindAddress");
+ QTest::addColumn<bool>("bindResult");
+ QTest::addColumn<QHostAddress>("groupAddress");
+ QTest::addColumn<bool>("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<QByteArray> datagrams = QList<QByteArray>()
+ << 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<QByteArray> 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<bool>("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 <QtNetwork>
+
+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
--- /dev/null
+++ b/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.der
Binary files 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
--- /dev/null
+++ b/tests/auto/network/ssl/qsslcertificate/certificates/cert-ss.der.pubkey
Binary files 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
--- /dev/null
+++ b/tests/auto/network/ssl/qsslcertificate/certificates/cert.der
Binary files 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
--- /dev/null
+++ b/tests/auto/network/ssl/qsslcertificate/certificates/cert.der.pubkey
Binary files 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 <QtTest/QtTest>
+#include <qsslcertificate.h>
+#include <qsslkey.h>
+#include <qsslsocket.h>
+
+#ifdef Q_OS_SYMBIAN
+// In Symbian OS test data is located in applications private dir
+// Current path (C:\private\<UID>) 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<CertInfo> certInfoList;
+ QMap<QString, QString> subjAltNameMap;
+ QMap<QString, QString> pubkeyMap;
+ QMap<QString, QString> md5Map;
+ QMap<QString, QString> 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<QSslError>&);
+
+// ### 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<QSsl::AlternativeNameEntryType, QString>()));
+#ifndef QT_NO_TEXTSTREAM
+ QCOMPARE(certificate.effectiveDate(), QDateTime());
+ QCOMPARE(certificate.expiryDate(), QDateTime());
+#endif
+}
+
+Q_DECLARE_METATYPE(QSsl::EncodingFormat);
+
+void tst_QSslCertificate::createTestRows()
+{
+ QTest::addColumn<QString>("absFilePath");
+ QTest::addColumn<QSsl::EncodingFormat>("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<QString>("absFilePath");
+ QTest::addColumn<QSsl::EncodingFormat>("format");
+ QTest::addColumn<QString>("absFilePath_digest_md5");
+ QTest::addColumn<QString>("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<QString>("certFilePath");
+ QTest::addColumn<QSsl::EncodingFormat>("format");
+ QTest::addColumn<QString>("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<QSsl::AlternativeNameEntryType, QString> altSubjectNames =
+ certificate.subjectAlternativeNames();
+
+ // verify that each entry in subjAltNames is present in fileContents
+ QMapIterator<QSsl::AlternativeNameEntryType, QString> 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<QString>("certFilePath");
+ QTest::addColumn<QSsl::EncodingFormat>("format");
+ QTest::addColumn<QString>("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<QSslCertificate> certs = QSslCertificate::fromDevice(0); // don't crash
+ QVERIFY(certs.isEmpty());
+}
+
+void tst_QSslCertificate::fromPath_data()
+{
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<int>("syntax");
+ QTest::addColumn<bool>("pemencoding");
+ QTest::addColumn<int>("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<QSslCertificate> 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<QSslCertificate> certList =
+ QSslCertificate::fromPath(SRCDIR "more-certificates/badguy-nul-san.crt");
+ QCOMPARE(certList.size(), 1);
+
+ const QSslCertificate &cert = certList.at(0);
+ QVERIFY(!cert.isNull());
+
+ QMultiMap<QSsl::AlternativeNameEntryType, QString> 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<QSslCertificate> 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<QSslCertificate> 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<QSslCertificate> 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<QSslCertificate> 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<QSslCertificate> 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<QSslCertificate> 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<QByteArray> 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<QSslError> errors;
+ QList<QSslCertificate> 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<QSslCertificate> 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<QSslError>& 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 <QtTest/QtTest>
+#include <qsslcipher.h>
+
+#include <QtNetwork/qhostaddress.h>
+#include <QtNetwork/qnetworkproxy.h>
+
+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 <QtTest/QtTest>
+#include <qsslerror.h>
+
+#include <QtNetwork/qhostaddress.h>
+#include <QtNetwork/qnetworkproxy.h>
+
+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
--- /dev/null
+++ b/tests/auto/network/ssl/qsslkey/keys/dsa-pri-1024.der
Binary files 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
--- /dev/null
+++ b/tests/auto/network/ssl/qsslkey/keys/dsa-pri-512.der
Binary files 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
--- /dev/null
+++ b/tests/auto/network/ssl/qsslkey/keys/dsa-pri-576.der
Binary files 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
--- /dev/null
+++ b/tests/auto/network/ssl/qsslkey/keys/dsa-pri-960.der
Binary files 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
--- /dev/null
+++ b/tests/auto/network/ssl/qsslkey/keys/dsa-pub-1024.der
Binary files 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
--- /dev/null
+++ b/tests/auto/network/ssl/qsslkey/keys/dsa-pub-512.der
Binary files 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
--- /dev/null
+++ b/tests/auto/network/ssl/qsslkey/keys/dsa-pub-576.der
Binary files 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
--- /dev/null
+++ b/tests/auto/network/ssl/qsslkey/keys/dsa-pub-960.der
Binary files 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
--- /dev/null
+++ b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-1023.der
Binary files 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
--- /dev/null
+++ b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-1024.der
Binary files 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
--- /dev/null
+++ b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-2048.der
Binary files 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
--- /dev/null
+++ b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-40.der
Binary files 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
--- /dev/null
+++ b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-511.der
Binary files 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
--- /dev/null
+++ b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-512.der
Binary files 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
--- /dev/null
+++ b/tests/auto/network/ssl/qsslkey/keys/rsa-pri-999.der
Binary files 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
--- /dev/null
+++ b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-1023.der
Binary files 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
--- /dev/null
+++ b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-1024.der
Binary files 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
--- /dev/null
+++ b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-2048.der
Binary files 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
--- /dev/null
+++ b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-40.der
Binary files 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
--- /dev/null
+++ b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-511.der
Binary files 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
--- /dev/null
+++ b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-512.der
Binary files 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
--- /dev/null
+++ b/tests/auto/network/ssl/qsslkey/keys/rsa-pub-999.der
Binary files 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 <QtTest/QtTest>
+#include <qsslkey.h>
+#include <qsslsocket.h>
+
+#include <QtNetwork/qhostaddress.h>
+#include <QtNetwork/qnetworkproxy.h>
+
+#ifdef Q_OS_SYMBIAN
+// In Symbian OS test data is located in applications private dir
+// Current path (C:\private\<UID>) 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<KeyInfo> 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<QString>("absFilePath");
+ QTest::addColumn<QSsl::KeyAlgorithm>("algorithm");
+ QTest::addColumn<QSsl::KeyType>("type");
+ QTest::addColumn<int>("length");
+ QTest::addColumn<QSsl::EncodingFormat>("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<QString>("absFilePath");
+ QTest::addColumn<QSsl::KeyAlgorithm>("algorithm");
+ QTest::addColumn<QSsl::KeyType>("type");
+ QTest::addColumn<QSsl::EncodingFormat>("format");
+ QTest::addColumn<QString>("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
--- /dev/null
+++ b/tests/auto/network/ssl/qsslsocket/ssl.tar.gz
Binary files 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 <QtCore/qthread.h>
+#include <QtNetwork/qhostaddress.h>
+#include <QtNetwork/qhostinfo.h>
+#include <QtNetwork/qnetworkproxy.h>
+#include <QtNetwork/qsslcipher.h>
+#include <QtNetwork/qsslconfiguration.h>
+#include <QtNetwork/qsslkey.h>
+#include <QtNetwork/qsslsocket.h>
+#include <QtNetwork/qtcpserver.h>
+#include <QtTest/QtTest>
+
+#include <QNetworkProxy>
+#include <QAuthenticator>
+
+#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<QSslError::SslError> 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<QSslSocket>
+{
+public:
+ inline QSslSocketPtr(QSslSocket *ptr = 0)
+ : QSharedPointer<QSslSocket>(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<QSslError> &errors)
+ {
+ if (errors.size() == 1 &&
+ (errors.first().error() == QSslError::CertificateUntrusted ||
+ errors.first().error() == QSslError::SelfSignedCertificate))
+ socket->ignoreSslErrors();
+ }
+ void ignoreErrorListSlot(const QList<QSslError> &errors);
+
+private:
+ QSslSocket *socket;
+ QList<QSslError> storedExpectedSslErrors;
+#endif // QT_NO_OPENSSL
+private:
+ static int loopLevel;
+};
+
+int tst_QSslSocket::loopLevel = 0;
+
+tst_QSslSocket::tst_QSslSocket()
+{
+#ifndef QT_NO_OPENSSL
+ qRegisterMetaType<QList<QSslError> >("QList<QSslError>");
+ qRegisterMetaType<QSslError>("QSslError");
+ qRegisterMetaType<QAbstractSocket::SocketState>("QAbstractSocket::SocketState");
+ qRegisterMetaType<QAbstractSocket::SocketError>("QAbstractSocket::SocketError");
+ qRegisterMetaType<QAbstractSocket::SocketState>("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<bool>("setProxy");
+ QTest::addColumn<int>("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<QSslCertificate>());
+ QSslSocket::setDefaultCiphers(QList<QSslCipher>());
+ 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<QSslError> &)));
+
+ 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<QSslError> &)), 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<QSslError> &)));
+
+ 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<QSslError> &)), this, SLOT(ignoreErrorSlot()));
+ connect(&socket, SIGNAL(sslErrors(const QList<QSslError> &)), 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<QString>("host");
+ QTest::addColumn<int>("port");
+ QTest::addColumn<SslErrorList>("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<QSslCipher>());
+ 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<QSslError>)),
+ this, SLOT(untrustedWorkaroundSlot(QList<QSslError>)));
+#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<QSslError>)),
+ this, SLOT(untrustedWorkaroundSlot(QList<QSslError>)));
+#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<QSslError>)), 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<QSslCertificate> 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<QSslCertificate> 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<QSslError>)),
+ this, SLOT(untrustedWorkaroundSlot(QList<QSslError>)));
+#endif
+
+ socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443);
+ QCOMPARE(socket->mode(), QSslSocket::UnencryptedMode);
+ QVERIFY(socket->peerCertificateChain().isEmpty());
+ QVERIFY2(socket->waitForEncrypted(10000), qPrintable(socket->errorString()));
+
+ QList<QSslCertificate> 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<RSA *>(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<QSslCertificate> 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<Qt::HANDLE>(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<QSslCertificate> certs = QSslCertificate::fromPath(SRCDIR "certs/qt-test-server-cacert.pem");
+
+ socket->setCaCertificates(certs);
+#ifdef QSSLSOCKET_CERTUNTRUSTED_WORKAROUND
+ connect(socket, SIGNAL(sslErrors(QList<QSslError>)),
+ this, SLOT(untrustedWorkaroundSlot(QList<QSslError>)));
+#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<QSslError> &)), 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<QSslCertificate> 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<QSsl::SslProtocol>("serverProtocol");
+ QTest::addColumn<QSsl::SslProtocol>("clientProtocol");
+ QTest::addColumn<bool>("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<QSslError> &)), 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<QSslError> &)), 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<QSslConfiguration>("configuration");
+ QTest::addColumn<bool>("works");
+
+ QTest::newRow("empty") << QSslConfiguration() << false;
+ QSslConfiguration conf = QSslConfiguration::defaultConfiguration();
+ QTest::newRow("default") << conf << false; // does not contain test server cert
+ QList<QSslCertificate> 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<QSslError> &)), 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<QSslError> &)), 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<QSslError> &)), 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<QSslCertificate> flukeCerts = QSslCertificate::fromPath(SRCDIR "certs/qt-test-server-cacert.pem");
+ QCOMPARE(flukeCerts.size(), 1);
+ QList<QSslCertificate> 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<QSslCertificate> 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<QSslCipher> 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<QSslCertificate> 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<QSslError>)),
+ this, SLOT(untrustedWorkaroundSlot(QList<QSslError>)));
+#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<QSslCertificate> 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<QSslError> &)), 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<QSslCertificate> 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<QSslError> expectedErrors = QList<QSslError>()
+ << 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<QSslError>)));
+ 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<QSslError>(peerVerifyErrorSpy.last().at(0)).error(), QSslError::HostNameMismatch);
+ QCOMPARE(qVariantValue<QList<QSslError> >(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<QList<QSslError> >("expectedSslErrors");
+ QTest::addColumn<int>("expectedSslErrorSignalCount");
+
+ // construct the list of errors that we will get with the SSL handshake and that we will ignore
+ QList<QSslError> expectedSslErrors;
+ // fromPath gives us a list of certs, but it actually only contains one
+ QList<QSslCertificate> 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<QSslError>, 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<QSslError> &)
+{
+ socket->ignoreSslErrors(storedExpectedSslErrors);
+}
+
+void tst_QSslSocket::ignoreSslErrorsListWithSlot()
+{
+ QSslSocket socket;
+ this->socket = &socket;
+
+ QFETCH(QList<QSslError>, 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<QSslError> &)),
+ this, SLOT(ignoreErrorListSlot(const QList<QSslError> &)));
+ 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<QSslError> &)), 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<QSslError>)), SLOT(exitLoop()));
+ connect(receiver, SIGNAL(encrypted()), SLOT(exitLoop()));
+ enterLoop(1);
+ QList<QSslError> 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<QSslError> &)), 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 \
+