ソースを参照

Added test to check ssl errors handling ( #111 )

Viktor Kopp 5 年 前
コミット
a6ae63d211

+ 6 - 0
src/grpc/http2channel.cpp

@@ -126,6 +126,12 @@ struct Http2ChannelPrivate {
 
         QNetworkReply *networkReply = nm.post(request, msg);
 
+        QObject::connect(networkReply, &QNetworkReply::sslErrors, [networkReply](const QList<QSslError> &errors) {
+           qProtoCritical() << errors;
+           // TODO: filter out noncritical SSL handshake errors
+           Http2ChannelPrivate::abortNetworkReply(networkReply);
+        });
+
         if (!stream) {
             //TODO: Add configurable timeout logic
             QTimer::singleShot(6000, networkReply, [networkReply]() {

+ 1 - 1
src/protobuf/qprotobufobject_p.h

@@ -708,7 +708,7 @@ public:
     }
 
     // this set of 3 methods is used to skip bytes corresponding to an unexpected property
-    // in a serialized message met while the message being serialized
+    // in a serialized message met while the message being deserialized
     static void skipVarint(SelfcheckIterator &it);
     static void skipLengthDelimited(SelfcheckIterator &it);
     static int skipSerializedFieldBytes(SelfcheckIterator &it, WireTypes type);

+ 10 - 8
tests/test_grpc/CMakeLists.txt

@@ -4,23 +4,25 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/testfile ${CMAKE_CURRENT_BINARY_DIR}/
 
 include(${CMAKE_SOURCE_DIR}/cmake/QtProtobufCommon.cmake)
 
-set(TARGET qtgrpc_test)
 set(GENERATED_HEADERS
     simplestringmessage.h
     testserviceclient.h
     blobmessage.h
-#    testserviceserver.h
+#   testserviceserver.h
 )
 
-file(GLOB SOURCES main.cpp
-#    servertest.cpp
-    clienttest.cpp)
-
-add_test_target(TARGET ${TARGET}
+# clients
+add_test_target(TARGET qtgrpc_test
+    GENERATED_HEADERS ${GENERATED_HEADERS}
+    SOURCES clienttest.cpp)
+    
+add_test_target(TARGET qtgrpc_secure_test
     GENERATED_HEADERS ${GENERATED_HEADERS}
-    SOURCES ${SOURCES})
+    SOURCES sslclienttest.cpp)
 
+# servers
 add_subdirectory(echoserver)
+add_subdirectory(secureechoserver)
 
 add_test(NAME qtgrpc_echo_test
          COMMAND test_driver.sh ./tests/test_grpc/qtgrpc_test ./tests/test_grpc/echoserver/echoserver

+ 2 - 0
tests/test_grpc/clienttest.cpp

@@ -27,10 +27,12 @@
 #include "http2channel.h"
 #include "insecurecredentials.h"
 #include "blobmessage.h"
+#include <sslcredentials.h>
 
 #include <QTimer>
 #include <QFile>
 #include <QCryptographicHash>
+#include <QSslConfiguration>
 
 #include <QCoreApplication>
 #include <gtest/gtest.h>

+ 21 - 0
tests/test_grpc/secureechoserver/CMakeLists.txt

@@ -0,0 +1,21 @@
+set(TARGET secureechoserver)
+
+set(GENERATED_SOURCES
+    ${CMAKE_CURRENT_BINARY_DIR}/simpletest.grpc.pb.cc
+    ${CMAKE_CURRENT_BINARY_DIR}/simpletest.pb.cc
+    ${CMAKE_CURRENT_BINARY_DIR}/testservice.grpc.pb.cc
+    ${CMAKE_CURRENT_BINARY_DIR}/testservice.pb.cc)
+set_source_files_properties(${GENERATED_SOURCES} PROPERTIES GENERATED TRUE)
+
+add_executable(${TARGET} main.cpp ${GENERATED_SOURCES})
+target_link_libraries(${TARGET} gRPC::grpc++ protobuf::libprotobuf)
+target_include_directories(${TARGET} PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
+if(MSVC)
+   target_compile_definitions(${TARGET} PRIVATE _WIN32_WINNT=0x600 _SCL_SECURE_NO_WARNINGS _CRT_SECURE_NO_WARNINGS _WINSOCK_DEPRECATED_NO_WARNINGS)
+endif()
+file(GLOB PROTO_FILES ABSOLUTE ${CMAKE_CURRENT_SOURCE_DIR}/../proto/*.proto)
+protobuf_generate_all(TARGET ${TARGET}
+    GENERATED_SOURCES ${GENERATED_SOURCES}
+    PROTO_FILES ${PROTO_FILES})
+    
+file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/cert.pem ${CMAKE_CURRENT_SOURCE_DIR}/key.pem DESTINATION ${CMAKE_CURRENT_BINARY_DIR})

+ 34 - 0
tests/test_grpc/secureechoserver/cert.pem

@@ -0,0 +1,34 @@
+-----BEGIN CERTIFICATE-----
+MIIF6zCCA9OgAwIBAgIJAMPfLBc9ERZGMA0GCSqGSIb3DQEBCwUAMIGLMQswCQYD
+VQQGEwJERTEPMA0GA1UECAwGQmVybGluMQ8wDQYDVQQHDAZCZXJsaW4xEzARBgNV
+BAoMClF0UHJvdG9idWYxDDAKBgNVBAsMA1JuRDESMBAGA1UEAwwJbG9jYWxob3N0
+MSMwIQYJKoZIhvcNAQkBFhRxdHByb3RvYnVmQGdtYWlsLmNvbTAeFw0xOTA0MjMx
+NTI5MzNaFw0yMDA0MjIxNTI5MzNaMIGLMQswCQYDVQQGEwJERTEPMA0GA1UECAwG
+QmVybGluMQ8wDQYDVQQHDAZCZXJsaW4xEzARBgNVBAoMClF0UHJvdG9idWYxDDAK
+BgNVBAsMA1JuRDESMBAGA1UEAwwJbG9jYWxob3N0MSMwIQYJKoZIhvcNAQkBFhRx
+dHByb3RvYnVmQGdtYWlsLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
+ggIBANCB7zdc6v+gsYCsGIYsT3iyGPgvFG7cJJWnQO9iW6Tn/kBcreYuCdVSSODK
+/NtPaA1r8j9FCDvLkzewNQ2Idv08/oKBYfOaVtQvh1cn7ktr1usiXtYv16cimeKk
+8iYbiczkZOah2xq+ivm9+05WkYTzcSjBpXg19894024GHd7oRV9G5MCr760k8YLM
+ALnoPpTl0yfs5cEcTybqvZFZNqkHDX2ziEbcF/mVcxcyEsmenbX6MI0easg2qZeq
+Sb7AW7tIMVoWUxDkUIor4vogbgU2IljAjzn0i+fPncB41TU5IiIU35vO67kFBXyI
+Ms3LpN1+Siz5HYHjwaWl0ecebuT83kP33VNc1ULkKJG5UbZUYfgZjUwZPgFFzVzQ
+cif0mhYj9s5Nmn6Q/twxeIXOIZAQdLOq625Wwx8bh+mGaV5mtw3wtXJjbceGa1z/
+Pnni4x2B7IfSiOzGLZZNRRHIjUskeONHUHn8YBrLLg5RT+tvPnGDVPh0cKEH2P4F
+cNRPmaqg75siFoJ+m3DlFM952tRcfzJumgbUfEnrL8bxTZPYVoq22qCDm3UGNW8n
+pthv8Y2hemv6V9lF050z2vpL2DtU9RmtkBWx7ipPUdsEOm4e0rdOCk7zo8IAiWMQ
+6o1L23IPTsqaEpqk3b7tT+5dtLkpeomAT8Hz91ChWUb6jrTVAgMBAAGjUDBOMB0G
+A1UdDgQWBBQIb2RjqkeMsUjH9QJ5BYnAtJ/vojAfBgNVHSMEGDAWgBQIb2RjqkeM
+sUjH9QJ5BYnAtJ/vojAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQDG
+2x+I9C+4XFCpg9KVA+BBi6+7yi4ulH0yv7W3hNWBV2MxTpvVN6RIt8xI1sPFXc2T
+g2wCIAIPGIPNDe8hO3gcfIIe059P+J3ZNjybUg5p6CR9O7kqgqV/nL4gVRuBJeYq
+6YLr1lKL9r3Zdbkq1tcaCaj816zdXVky0us72XP28/xJc96crgKzDI69vESy5jq6
+QT/HoBwYiSaWXNgb0zJzc//e0upXSLeTkYizAJ5OGkQ/MQYE7gDvtPlGVhQ0rnl/
+FLsiZJokuxtLOTvYJ8Gynjz3QwbClN/bmUbOgD0fqW6BEZyZSJ4zCz0BJnwg46gc
+IN+p6vi50MG4ZcJcnMl/3tAxt2RxHNLi0j21NSLyFj60gK+vL3/zzkdYF9ZxX1L+
+dqhTqioCVrV96rJIQbz6JrbFhUCdyEHYG7yi6LxHTUex33XouhGAfZ0lri5wWZq8
+0Rx8PEZ7SbjARtnvA7uXIAfgD+n3oqnkg9IDPH8PbdRcJrAdje3O2c1+OzTAC8ni
+czaCWG748gfZPe0JpENP7P56RTh9avj4sHISCx4r+sh0lruLp2JZPr9qtw09uWlq
+Mn58bcnDu9RjVfPXk43s9WfJC9XII+JoNcW3iJDSHxQb9XLvwR9ntWhkVvOKenPP
+b00kWr6FfId6fiOmUww5WkW+Wtt4XOYsypaN+DskeA==
+-----END CERTIFICATE-----

+ 52 - 0
tests/test_grpc/secureechoserver/key.pem

@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+0mVZG9D69wbsZefVDJii9NLvWThGog==
+-----END PRIVATE KEY-----

+ 89 - 0
tests/test_grpc/secureechoserver/main.cpp

@@ -0,0 +1,89 @@
+#include <iostream>
+#include <grpc++/grpc++.h>
+#include <testservice.pb.h>
+#include <testservice.grpc.pb.h>
+#include <simpletest.pb.h>
+#include <simpletest.grpc.pb.h>
+#include <thread>
+#include <fstream>
+#include <sstream>
+
+class SimpleTestImpl final : public qtprotobufnamespace::tests::TestService::Service {
+public:
+    ::grpc::Status testMethod(grpc::ServerContext *, const qtprotobufnamespace::tests::SimpleStringMessage *request, qtprotobufnamespace::tests::SimpleStringMessage *response) override
+    {
+        std::cerr << "testMethod called" << std::endl << request->testfieldstring() << std::endl;
+        response->set_testfieldstring(request->testfieldstring());
+        if(request->testfieldstring() == "sleep") {
+            std::this_thread::sleep_for(std::chrono::seconds(3));
+        }
+        return ::grpc::Status();
+    }
+
+    ::grpc::Status testMethodServerStream(grpc::ServerContext *, const qtprotobufnamespace::tests::SimpleStringMessage *request,
+                                          ::grpc::ServerWriter<qtprotobufnamespace::tests::SimpleStringMessage> *writer) override
+    {
+        std::cerr << "testMethodServerStream called" << std::endl << request->testfieldstring() << std::endl;
+        qtprotobufnamespace::tests::SimpleStringMessage msg;
+
+        msg.set_testfieldstring(request->testfieldstring() + "1");
+        std::this_thread::sleep_for(std::chrono::seconds(3));
+        std::cerr << "send back " << (request->testfieldstring() + "1") << std::endl;
+        writer->Write(msg);
+
+        msg.set_testfieldstring(request->testfieldstring() + "2");
+        std::this_thread::sleep_for(std::chrono::seconds(3));
+        std::cerr << "send back " << (request->testfieldstring() + "2") << std::endl;
+        writer->Write(msg);
+
+        msg.set_testfieldstring(request->testfieldstring() + "3");
+        std::this_thread::sleep_for(std::chrono::seconds(3));
+        std::cerr << "send back " << (request->testfieldstring() + "3") << std::endl;
+        writer->Write(msg);
+
+        msg.set_testfieldstring(request->testfieldstring() + "4");
+        std::this_thread::sleep_for(std::chrono::seconds(3));
+        std::cerr << "send back " << (request->testfieldstring() + "4") << std::endl;
+        writer->WriteLast(msg, grpc::WriteOptions());
+
+        return ::grpc::Status();
+    }
+
+    ::grpc::Status testMethodBlobServerStream(grpc::ServerContext *, const qtprotobufnamespace::tests::BlobMessage *request,
+                                          ::grpc::ServerWriter<qtprotobufnamespace::tests::BlobMessage> *writer) override
+    {
+        std::cerr << "testMethodBlobServerStream called" << std::endl;
+        qtprotobufnamespace::tests::BlobMessage msg;
+        msg.set_allocated_testbytes(new std::string(request->testbytes()));
+        writer->Write(msg);
+        return ::grpc::Status();
+    }
+};
+
+int main(int, char *[])
+{
+    std::ifstream tfile("cert.pem");
+    std::stringstream cert;
+    cert << tfile.rdbuf();
+    tfile.close();
+
+    tfile.open("key.pem");
+    std::stringstream key;
+    key << tfile.rdbuf();
+    tfile.close();
+
+    grpc::ServerBuilder builder;
+    grpc::SslServerCredentialsOptions opts(GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE);
+    opts.pem_key_cert_pairs.push_back({key.str(), cert.str()});
+
+    std::string server_uri("localhost:60051");
+    builder.AddListeningPort(server_uri, grpc::SslServerCredentials(opts));
+
+    SimpleTestImpl service;
+    builder.RegisterService(&service);
+
+    std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
+    std::cout << "Server listening on " << server_uri << std::endl;
+
+    server->Wait();
+}

+ 77 - 0
tests/test_grpc/sslclienttest.cpp

@@ -0,0 +1,77 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of qtprotobuf project https://git.semlanik.org/semlanik/qtprotobuf
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "testserviceclient.h"
+#include "http2channel.h"
+#include "blobmessage.h"
+#include <sslcredentials.h>
+
+#include <QTimer>
+#include <QFile>
+#include <QCryptographicHash>
+#include <QSslConfiguration>
+
+#include <QCoreApplication>
+#include <gtest/gtest.h>
+
+using namespace qtprotobufnamespace::tests;
+using namespace qtprotobuf;
+
+class ClientTest : public ::testing::Test
+{
+protected:
+    static QCoreApplication m_app;
+    static int m_argc;
+    static QUrl m_echoServerAddress;
+};
+
+int ClientTest::m_argc(0);
+QCoreApplication ClientTest::m_app(m_argc, nullptr);
+QUrl ClientTest::m_echoServerAddress("https://localhost:60051", QUrl::StrictMode);
+
+TEST_F(ClientTest, IncorrectSecureCredentialsTest)
+{
+    //Prepare ssl configuration
+    QSslConfiguration conf = QSslConfiguration::defaultConfiguration();
+
+    //NOTE: CA certificate setup is ommited on purpose
+    //  QFile certFile("cert.pem");
+    //  certFile.open(QIODevice::ReadOnly);
+    //  QByteArray cert = certFile.readAll();
+    //  conf.setCaCertificates({QSslCertificate(cert)});
+
+    conf.setProtocol(QSsl::TlsV1_2);
+    //conf.setAllowedNextProtocols({QSslConfiguration::ALPNProtocolHTTP2});
+
+    std::shared_ptr<qtprotobuf::AbstractChannel> channel(new qtprotobuf::Http2Channel(m_echoServerAddress, qtprotobuf::SslCredentials(conf)));
+
+    TestServiceClient testClient;
+    testClient.attachChannel(channel);
+
+    QPointer<SimpleStringMessage> result(new SimpleStringMessage);
+    EXPECT_FALSE(testClient.testMethod(SimpleStringMessage{"Hello beach!"}, result));
+
+    delete result;
+}