Pārlūkot izejas kodu

use github repo as central

Jackson Tian 7 gadi atpakaļ
vecāks
revīzija
bc0df2dcec
46 mainītis faili ar 1864 papildinājumiem un 29 dzēšanām
  1. 1 0
      .gitignore
  2. 13 4
      core/CMakeLists.txt
  3. 3 3
      core/include/alibabacloud/core/HttpRequest.h
  4. 5 5
      core/src/AlibabaCloud.cc
  5. 1 1
      core/src/Executor.cc
  6. 6 5
      core/src/HttpMessage.cc
  7. 1 1
      core/src/StsAssumeRoleCredentialsProvider.cc
  8. 7 7
      core/src/Utils.cc
  9. 3 3
      core/src/Utils.h
  10. 0 0
      productcatalogtestcasehasversionhasapipass/readme.md
  11. 24 0
      test-coverage.sh
  12. 69 0
      test/core/CMakeLists.txt
  13. 17 0
      test/core/DownloadProject.CMakeLists.cmake.in
  14. 182 0
      test/core/DownloadProject.cmake
  15. 18 0
      test/core/alibabacloud_ut.cc
  16. 18 0
      test/core/asynccallercontext_ut.cc
  17. 31 0
      test/core/clientconfiguration_ut.cc
  18. 22 0
      test/core/commonrequest_ut.cc
  19. 14 0
      test/core/commonresponse_ut.cc
  20. 28 0
      test/core/credentials_ut.cc
  21. 145 0
      test/core/curlhttpclient_ut.cc
  22. 69 0
      test/core/endpointprovider_ut.cc
  23. 33 0
      test/core/error_ut.cc
  24. 78 0
      test/core/executor_ut.cc
  25. 25 0
      test/core/hmacsha1signer_ut.cc
  26. 56 0
      test/core/httpmessage_ut.cc
  27. 30 0
      test/core/httprequest_ut.cc
  28. 18 0
      test/core/httpresponse_ut.cc
  29. 21 0
      test/core/networkproxy_ut.cc
  30. 16 0
      test/core/roaservicerequest_ut.cc
  31. 18 0
      test/core/rpcservicerequest_ut.cc
  32. 22 0
      test/core/runnable_ut.cc
  33. 110 0
      test/core/servicerequest_ut.cc
  34. 29 0
      test/core/serviceresult_ut.cc
  35. 20 0
      test/core/signer_ut.cc
  36. 23 0
      test/core/simplecredentialsprovider_ut.cc
  37. 118 0
      test/core/url_ut.cc
  38. 30 0
      test/core/utils.h
  39. 63 0
      test/core/utils_ut.cc
  40. 21 0
      test/httpserver/CMakeLists.txt
  41. 118 0
      test/httpserver/HTTPServer.cpp
  42. 52 0
      test/httpserver/HTTPServer.h
  43. 163 0
      test/httpserver/ServerThread.cpp
  44. 55 0
      test/httpserver/ServerThread.h
  45. 1 0
      test/httpserver/index.html
  46. 67 0
      test/httpserver/main.cpp

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+.vscode/

+ 13 - 4
core/CMakeLists.txt

@@ -1,12 +1,12 @@
 #
 # Copyright 2009-2017 Alibaba Cloud All rights reserved.
-# 
+#
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
 # You may obtain a copy of the License at
-# 
+#
 #      http://www.apache.org/licenses/LICENSE-2.0
-# 
+#
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -14,6 +14,15 @@
 # limitations under the License.
 #
 
+# coverage option
+OPTION (ENABLE_COVERAGE "Use gcov" ON)
+MESSAGE(STATUS ENABLE_COVERAGE=${ENABLE_COVERAGE})
+IF(ENABLE_COVERAGE)
+    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage")
+    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage")
+#    SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage")
+ENDIF()
+
 configure_file(src/Config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/include/alibabacloud/core/Config.h @ONLY)
 
 set(core_public_header
@@ -120,7 +129,7 @@ set(core_src
 	src/sts/StsClient.cc
 	src/sts/StsRequest.cc
 	src/sts/model/AssumeRoleRequest.cc
-	src/sts/model/AssumeRoleResult.cc	
+	src/sts/model/AssumeRoleResult.cc
 	src/sts/model/GetCallerIdentityRequest.cc
 	src/sts/model/GetCallerIdentityResult.cc
 	)

+ 3 - 3
core/include/alibabacloud/core/HttpRequest.h

@@ -1,12 +1,12 @@
 /*
  * Copyright 2009-2017 Alibaba Cloud All rights reserved.
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *      http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ 5 - 5
core/src/AlibabaCloud.cc

@@ -1,12 +1,12 @@
 /*
  * Copyright 2009-2017 Alibaba Cloud All rights reserved.
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *      http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -18,7 +18,7 @@
 #include "Executor.h"
 
 static AlibabaCloud::Executor * executor = nullptr;
- 
+
 void AlibabaCloud::InitializeSdk()
 {
 	if (IsSdkInitialized())
@@ -37,7 +37,7 @@ void AlibabaCloud::ShutdownSdk()
 {
 	if (!IsSdkInitialized())
 		return;
-	
+
 	executor->shutdown();
 	delete executor;
 	executor = nullptr;

+ 1 - 1
core/src/Executor.cc

@@ -106,7 +106,7 @@ void Executor::shutdown()
 {
 	if (isShutdown())
 		return;
-	
+
 	{
 		std::lock_guard<std::mutex> locker(tasksQueueMutex_);
 		while (tasksQueue_.size() > 0) {

+ 6 - 5
core/src/HttpMessage.cc

@@ -1,12 +1,12 @@
 /*
  * Copyright 2009-2017 Alibaba Cloud All rights reserved.
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *      http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -99,7 +99,7 @@ HttpMessage::HeaderValueType HttpMessage::header(const HeaderNameType & name) co
 	auto it = headers_.find(name);
 	if (it != headers_.end())
 		return it->second;
-	else	
+	else
 		return std::string();
 }
 
@@ -161,8 +161,9 @@ void HttpMessage::setBody(const char *data, size_t size)
 	bodySize_ = 0;
 	if (size) {
 		bodySize_ = size;
-		body_ = new char[size];
+		body_ = new char[size + 1];
 		std::copy(data, data + size, body_);
+		body_[size] = '\0';
 	}
 }
 

+ 1 - 1
core/src/StsAssumeRoleCredentialsProvider.cc

@@ -77,7 +77,7 @@ void StsAssumeRoleCredentialsProvider::loadCredentials()
 				std::tm tm = {};
 #if defined(__GNUG__) && __GNUC__ < 5
 				strptime(stsCredentials.expiration.c_str(), "%Y-%m-%dT%H:%M:%SZ", &tm);
-#else			
+#else
 				std::stringstream ss(stsCredentials.expiration);
 				ss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%SZ");
 #endif

+ 7 - 7
core/src/Utils.cc

@@ -1,12 +1,12 @@
 /*
  * Copyright 2009-2017 Alibaba Cloud All rights reserved.
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *      http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -45,7 +45,7 @@ std::string AlibabaCloud::GenerateUuid()
 
 std::string AlibabaCloud::UrlEncode(const std::string & src)
 {
-	CURL *curl = curl_easy_init();	
+	CURL *curl = curl_easy_init();
 	char *output = curl_easy_escape(curl, src.c_str(), src.size());
 	std::string result(output);
 	curl_free(output);
@@ -89,7 +89,7 @@ std::string AlibabaCloud::ComputeContentMD5(const char * data, size_t size)
 #else
 	unsigned char md[MD5_DIGEST_LENGTH];
 	MD5(reinterpret_cast<const unsigned char*>(data), size, (unsigned char*)&md);
-	 
+
 	char encodedData[100];
 	EVP_EncodeBlock(reinterpret_cast<unsigned char*>(encodedData), md, MD5_DIGEST_LENGTH);
 	return encodedData;
@@ -99,10 +99,10 @@ std::string AlibabaCloud::ComputeContentMD5(const char * data, size_t size)
 void AlibabaCloud::StringReplace(std::string & src, const std::string & s1, const std::string & s2)
 {
 	std::string::size_type pos =0;
-	while ((pos = src.find(s1, pos)) != std::string::npos) 
+	while ((pos = src.find(s1, pos)) != std::string::npos)
 	{
 		src.replace(pos, s1.length(), s2);
-		pos += s2.length(); 
+		pos += s2.length();
 	}
 }
 

+ 3 - 3
core/src/Utils.h

@@ -1,12 +1,12 @@
 /*
  * Copyright 2009-2017 Alibaba Cloud All rights reserved.
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *      http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ 0 - 0
productcatalogtestcasehasversionhasapipass/readme.md


+ 24 - 0
test-coverage.sh

@@ -0,0 +1,24 @@
+#!/bin/bash
+
+action=$1
+
+if [ $action == "init" ]; then
+  echo 'init coverage'
+
+  lcov -d sdk_build -z
+  lcov -d sdk_build -b . --no-external --initial -c -o initCoverage.info
+fi
+
+echo 'run test'
+cd sdk_build
+ctest
+cd ..
+
+echo 'create info after test'
+lcov -d sdk_build -b . --no-external -c -o testCoverage.info
+
+echo 'generate html'
+genhtml -o coverageReport --prefix=`pwd` initCoverage.info testCoverage.info
+
+echo 'check report ' `pwd`/coverageReport/index.html
+

+ 69 - 0
test/core/CMakeLists.txt

@@ -0,0 +1,69 @@
+cmake_minimum_required(VERSION 2.8.2)
+
+set(CMAKE_CXX_STANDARD 11)
+
+include(CTest)
+
+if (CMAKE_VERSION VERSION_LESS 3.2)
+  set(UPDATE_DISCONNECTED_IF_AVAILABLE "")
+else()
+  set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1")
+endif()
+
+include(DownloadProject.cmake)
+download_project(PROJ                googletest
+                 PREFIX              CMAKE_SOURCE_DIR/test/googletest
+                 GIT_REPOSITORY      https://github.com/google/googletest.git
+                 GIT_TAG             master
+                 ${UPDATE_DISCONNECTED_IF_AVAILABLE}
+)
+
+# Prevent GoogleTest from overriding our compiler/linker options
+# when building with Visual Studio
+set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
+
+add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
+
+# When using CMake 2.8.11 or later, header path dependencies
+# are automatically added to the gtest and gmock targets.
+# For earlier CMake versions, we have to explicitly add the
+# required directories to the header search path ourselves.
+if (CMAKE_VERSION VERSION_LESS 2.8.11)
+  include_directories("${gtest_SOURCE_DIR}/include"
+                      "${gmock_SOURCE_DIR}/include")
+endif()
+
+include_directories("../../core/include/")
+
+add_executable(core_ut
+  alibabacloud_ut.cc
+  asynccallercontext_ut.cc
+  clientconfiguration_ut.cc
+  commonrequest_ut.cc
+  commonresponse_ut.cc
+  credentials_ut.cc
+  curlhttpclient_ut.cc
+  endpointprovider_ut.cc
+  error_ut.cc
+  executor_ut.cc
+  httprequest_ut.cc
+  httpresponse_ut.cc
+  httpmessage_ut.cc
+  hmacsha1signer_ut.cc
+  networkproxy_ut.cc
+  roaservicerequest_ut.cc
+  rpcservicerequest_ut.cc
+  runnable_ut.cc
+  serviceresult_ut.cc
+  servicerequest_ut.cc
+  simplecredentialsprovider_ut.cc
+  url_ut.cc
+  utils_ut.cc)
+
+set_target_properties(core_ut
+  PROPERTIES
+  OUTPUT_NAME ${TARGET_OUTPUT_NAME_PREFIX}core_ut
+  RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
+
+target_link_libraries(core_ut core gtest gmock_main)
+add_test(NAME core_ut COMMAND core_ut)

+ 17 - 0
test/core/DownloadProject.CMakeLists.cmake.in

@@ -0,0 +1,17 @@
+# Distributed under the OSI-approved MIT License.  See accompanying
+# file LICENSE or https://github.com/Crascit/DownloadProject for details.
+
+cmake_minimum_required(VERSION 2.8.2)
+
+project(${DL_ARGS_PROJ}-download NONE)
+
+include(ExternalProject)
+ExternalProject_Add(${DL_ARGS_PROJ}-download
+                    ${DL_ARGS_UNPARSED_ARGUMENTS}
+                    SOURCE_DIR          "${DL_ARGS_SOURCE_DIR}"
+                    BINARY_DIR          "${DL_ARGS_BINARY_DIR}"
+                    CONFIGURE_COMMAND   ""
+                    BUILD_COMMAND       ""
+                    INSTALL_COMMAND     ""
+                    TEST_COMMAND        ""
+)

+ 182 - 0
test/core/DownloadProject.cmake

@@ -0,0 +1,182 @@
+# Distributed under the OSI-approved MIT License.  See accompanying
+# file LICENSE or https://github.com/Crascit/DownloadProject for details.
+#
+# MODULE:   DownloadProject
+#
+# PROVIDES:
+#   download_project( PROJ projectName
+#                    [PREFIX prefixDir]
+#                    [DOWNLOAD_DIR downloadDir]
+#                    [SOURCE_DIR srcDir]
+#                    [BINARY_DIR binDir]
+#                    [QUIET]
+#                    ...
+#   )
+#
+#       Provides the ability to download and unpack a tarball, zip file, git repository,
+#       etc. at configure time (i.e. when the cmake command is run). How the downloaded
+#       and unpacked contents are used is up to the caller, but the motivating case is
+#       to download source code which can then be included directly in the build with
+#       add_subdirectory() after the call to download_project(). Source and build
+#       directories are set up with this in mind.
+#
+#       The PROJ argument is required. The projectName value will be used to construct
+#       the following variables upon exit (obviously replace projectName with its actual
+#       value):
+#
+#           projectName_SOURCE_DIR
+#           projectName_BINARY_DIR
+#
+#       The SOURCE_DIR and BINARY_DIR arguments are optional and would not typically
+#       need to be provided. They can be specified if you want the downloaded source
+#       and build directories to be located in a specific place. The contents of
+#       projectName_SOURCE_DIR and projectName_BINARY_DIR will be populated with the
+#       locations used whether you provide SOURCE_DIR/BINARY_DIR or not.
+#
+#       The DOWNLOAD_DIR argument does not normally need to be set. It controls the
+#       location of the temporary CMake build used to perform the download.
+#
+#       The PREFIX argument can be provided to change the base location of the default
+#       values of DOWNLOAD_DIR, SOURCE_DIR and BINARY_DIR. If all of those three arguments
+#       are provided, then PREFIX will have no effect. The default value for PREFIX is
+#       CMAKE_BINARY_DIR.
+#
+#       The QUIET option can be given if you do not want to show the output associated
+#       with downloading the specified project.
+#
+#       In addition to the above, any other options are passed through unmodified to
+#       ExternalProject_Add() to perform the actual download, patch and update steps.
+#       The following ExternalProject_Add() options are explicitly prohibited (they
+#       are reserved for use by the download_project() command):
+#
+#           CONFIGURE_COMMAND
+#           BUILD_COMMAND
+#           INSTALL_COMMAND
+#           TEST_COMMAND
+#
+#       Only those ExternalProject_Add() arguments which relate to downloading, patching
+#       and updating of the project sources are intended to be used. Also note that at
+#       least one set of download-related arguments are required.
+#
+#       If using CMake 3.2 or later, the UPDATE_DISCONNECTED option can be used to
+#       prevent a check at the remote end for changes every time CMake is run
+#       after the first successful download. See the documentation of the ExternalProject
+#       module for more information. It is likely you will want to use this option if it
+#       is available to you. Note, however, that the ExternalProject implementation contains
+#       bugs which result in incorrect handling of the UPDATE_DISCONNECTED option when
+#       using the URL download method or when specifying a SOURCE_DIR with no download
+#       method. Fixes for these have been created, the last of which is scheduled for
+#       inclusion in CMake 3.8.0. Details can be found here:
+#
+#           https://gitlab.kitware.com/cmake/cmake/commit/bdca68388bd57f8302d3c1d83d691034b7ffa70c
+#           https://gitlab.kitware.com/cmake/cmake/issues/16428
+#
+#       If you experience build errors related to the update step, consider avoiding
+#       the use of UPDATE_DISCONNECTED.
+#
+# EXAMPLE USAGE:
+#
+#   include(DownloadProject)
+#   download_project(PROJ                googletest
+#                    GIT_REPOSITORY      https://github.com/google/googletest.git
+#                    GIT_TAG             master
+#                    UPDATE_DISCONNECTED 1
+#                    QUIET
+#   )
+#
+#   add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
+#
+#========================================================================================
+
+
+set(_DownloadProjectDir "${CMAKE_CURRENT_LIST_DIR}")
+
+include(CMakeParseArguments)
+
+function(download_project)
+
+    set(options QUIET)
+    set(oneValueArgs
+        PROJ
+        PREFIX
+        DOWNLOAD_DIR
+        SOURCE_DIR
+        BINARY_DIR
+        # Prevent the following from being passed through
+        CONFIGURE_COMMAND
+        BUILD_COMMAND
+        INSTALL_COMMAND
+        TEST_COMMAND
+    )
+    set(multiValueArgs "")
+
+    cmake_parse_arguments(DL_ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+    # Hide output if requested
+    if (DL_ARGS_QUIET)
+        set(OUTPUT_QUIET "OUTPUT_QUIET")
+    else()
+        unset(OUTPUT_QUIET)
+        message(STATUS "Downloading/updating ${DL_ARGS_PROJ}")
+    endif()
+
+    # Set up where we will put our temporary CMakeLists.txt file and also
+    # the base point below which the default source and binary dirs will be.
+    # The prefix must always be an absolute path.
+    if (NOT DL_ARGS_PREFIX)
+        set(DL_ARGS_PREFIX "${CMAKE_BINARY_DIR}")
+    else()
+        get_filename_component(DL_ARGS_PREFIX "${DL_ARGS_PREFIX}" ABSOLUTE
+                               BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}")
+    endif()
+    if (NOT DL_ARGS_DOWNLOAD_DIR)
+        set(DL_ARGS_DOWNLOAD_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-download")
+    endif()
+
+    # Ensure the caller can know where to find the source and build directories
+    if (NOT DL_ARGS_SOURCE_DIR)
+        set(DL_ARGS_SOURCE_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-src")
+    endif()
+    if (NOT DL_ARGS_BINARY_DIR)
+        set(DL_ARGS_BINARY_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-build")
+    endif()
+    set(${DL_ARGS_PROJ}_SOURCE_DIR "${DL_ARGS_SOURCE_DIR}" PARENT_SCOPE)
+    set(${DL_ARGS_PROJ}_BINARY_DIR "${DL_ARGS_BINARY_DIR}" PARENT_SCOPE)
+
+    # The way that CLion manages multiple configurations, it causes a copy of
+    # the CMakeCache.txt to be copied across due to it not expecting there to
+    # be a project within a project.  This causes the hard-coded paths in the
+    # cache to be copied and builds to fail.  To mitigate this, we simply
+    # remove the cache if it exists before we configure the new project.  It
+    # is safe to do so because it will be re-generated.  Since this is only
+    # executed at the configure step, it should not cause additional builds or
+    # downloads.
+    file(REMOVE "${DL_ARGS_DOWNLOAD_DIR}/CMakeCache.txt")
+
+    # Create and build a separate CMake project to carry out the download.
+    # If we've already previously done these steps, they will not cause
+    # anything to be updated, so extra rebuilds of the project won't occur.
+    # Make sure to pass through CMAKE_MAKE_PROGRAM in case the main project
+    # has this set to something not findable on the PATH.
+    configure_file("${_DownloadProjectDir}/DownloadProject.CMakeLists.cmake.in"
+                   "${DL_ARGS_DOWNLOAD_DIR}/CMakeLists.txt")
+    execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}"
+                        -D "CMAKE_MAKE_PROGRAM:FILE=${CMAKE_MAKE_PROGRAM}"
+                        .
+                    RESULT_VARIABLE result
+                    ${OUTPUT_QUIET}
+                    WORKING_DIRECTORY "${DL_ARGS_DOWNLOAD_DIR}"
+    )
+    if(result)
+        message(FATAL_ERROR "CMake step for ${DL_ARGS_PROJ} failed: ${result}")
+    endif()
+    execute_process(COMMAND ${CMAKE_COMMAND} --build .
+                    RESULT_VARIABLE result
+                    ${OUTPUT_QUIET}
+                    WORKING_DIRECTORY "${DL_ARGS_DOWNLOAD_DIR}"
+    )
+    if(result)
+        message(FATAL_ERROR "Build step for ${DL_ARGS_PROJ} failed: ${result}")
+    endif()
+
+endfunction()

+ 18 - 0
test/core/alibabacloud_ut.cc

@@ -0,0 +1,18 @@
+#include "gtest/gtest.h"
+#include "alibabacloud/core/AlibabaCloud.h"
+
+using namespace std;
+using namespace AlibabaCloud;
+
+
+TEST(AlibabaCloudTest, basic) {
+  InitializeSdk();
+  EXPECT_TRUE(IsSdkInitialized() == true);
+  InitializeSdk();
+  EXPECT_TRUE(IsSdkInitialized() == true);
+
+  ShutdownSdk();
+  EXPECT_TRUE(IsSdkInitialized() == false);
+  ShutdownSdk();
+  EXPECT_TRUE(IsSdkInitialized() == false);
+}

+ 18 - 0
test/core/asynccallercontext_ut.cc

@@ -0,0 +1,18 @@
+#include "gtest/gtest.h"
+#include "alibabacloud/core/AsyncCallerContext.h"
+
+using namespace std;
+using namespace AlibabaCloud;
+
+
+TEST(AsyncCallerContext, basic) {
+  const std::string sys_generated_uuid = "643ce9ff-32b5-444c-814a-153b87a6aca8";
+  const std::string init_uuid = "init_uuid_for_test";
+  const std::string new_uuid = "new_uuid_for_test";
+  AsyncCallerContext context(init_uuid);
+  EXPECT_TRUE(context.uuid() == init_uuid);
+  context.setUuid(new_uuid);
+  EXPECT_TRUE(context.uuid() == new_uuid);
+  AsyncCallerContext context_from_GenerateUuid = AsyncCallerContext();
+  EXPECT_TRUE(context_from_GenerateUuid.uuid().length() == sys_generated_uuid.length());
+}

+ 31 - 0
test/core/clientconfiguration_ut.cc

@@ -0,0 +1,31 @@
+#include "gtest/gtest.h"
+#include "alibabacloud/core/ClientConfiguration.h"
+
+using namespace std;
+using namespace AlibabaCloud;
+
+
+TEST(ClientConfiguration, basic) {
+  const std::string regionId = "cn-shanghai";
+  const std::string regionId_new = "cn-beijing";
+  const std::string endpoint = "ep-cn-shanghai";
+
+  const std::string hostname = "hostname";
+  const std::string user = "user";
+  const std::string password = "password";
+  uint16_t port = 12345;
+  const NetworkProxy proxy(NetworkProxy::Http, hostname, port, user, password);
+
+  ClientConfiguration config(regionId);
+  EXPECT_TRUE(config.regionId() == regionId);
+
+  config.setEndpoint(endpoint);
+  config.setProxy(proxy);
+  config.setRegionId(regionId_new);
+  EXPECT_TRUE(config.endpoint() == endpoint);
+  EXPECT_TRUE(config.regionId() == regionId_new);
+  EXPECT_TRUE(config.proxy().hostName() == hostname);
+  EXPECT_TRUE(config.proxy().port() == port);
+  EXPECT_TRUE(config.proxy().user() == user);
+  EXPECT_TRUE(config.proxy().password() == password);
+}

+ 22 - 0
test/core/commonrequest_ut.cc

@@ -0,0 +1,22 @@
+#include "gtest/gtest.h"
+#include "alibabacloud/core/CommonRequest.h"
+
+using namespace std;
+using namespace AlibabaCloud;
+
+TEST(CommonRequest, basic) {
+  CommonRequest cr;
+
+  cr.setDomain("testDomain");
+  cr.setHeaderParameter("name1", "value1");
+  cr.setHttpMethod(HttpRequest::Method::Get);
+  cr.setQueryParameter("name2", "value2");
+  cr.setRequestPattern(CommonRequest::RequestPattern::RpcPattern);
+  EXPECT_TRUE(cr.domain() == "testDomain");
+  EXPECT_TRUE(cr.httpMethod() == HttpRequest::Method::Get);
+  EXPECT_TRUE(cr.requestPattern() == CommonRequest::RequestPattern::RpcPattern);
+  EXPECT_TRUE(cr.headerParameter("name1") == "value1");
+  EXPECT_TRUE(cr.queryParameter("name2") == "value2");
+  EXPECT_TRUE(cr.queryParameters()["name2"] == "value2");
+  EXPECT_TRUE(cr.headerParameters()["name1"] == "value1");
+}

+ 14 - 0
test/core/commonresponse_ut.cc

@@ -0,0 +1,14 @@
+#include "gtest/gtest.h"
+#include "alibabacloud/core/CommonResponse.h"
+
+
+using namespace std;
+using namespace AlibabaCloud;
+
+TEST(CommonResponse, basic) {
+  CommonResponse cres("test_payload_");
+  EXPECT_TRUE(cres.payload() == "test_payload_");
+
+  CommonResponse cres1;
+  EXPECT_TRUE(cres1.payload() == "");
+}

+ 28 - 0
test/core/credentials_ut.cc

@@ -0,0 +1,28 @@
+#include "gtest/gtest.h"
+#include "alibabacloud/core/Credentials.h"
+
+using namespace std;
+using namespace AlibabaCloud;
+
+TEST(CredentialsTest, basic) {
+  const string access_key = "accessKeyId";
+  const string access_secret = "accessKeySecret";
+  const string session_token = "sessionToken";
+  const string new_access_key = "new_accessKeyId";
+  const string new_access_secret = "new_accessKeySecret";
+  const string new_session_token = "new_sessionToken";
+
+  Credentials credentials(access_key, access_secret, session_token);
+
+  EXPECT_TRUE(credentials.accessKeyId() == access_key);
+  EXPECT_TRUE(credentials.accessKeySecret() == access_secret);
+  EXPECT_TRUE(credentials.sessionToken() == session_token);
+
+  credentials.setAccessKeyId(new_access_key);
+  credentials.setAccessKeySecret(new_access_secret);
+  credentials.setSessionToken(new_session_token);
+
+  EXPECT_TRUE(credentials.accessKeyId() == new_access_key);
+  EXPECT_TRUE(credentials.accessKeySecret() == new_access_secret);
+  EXPECT_TRUE(credentials.sessionToken() == new_session_token);
+}

+ 145 - 0
test/core/curlhttpclient_ut.cc

@@ -0,0 +1,145 @@
+#include <iostream>
+#include <stdio.h>
+
+#include "utils.h"
+#include "../include/gtest/gtest.h"
+#include "../include/gmock/gmock.h"
+#include "../../core/include/alibabacloud/core/Url.h"
+#include "../../core/include/alibabacloud/core/HttpRequest.h"
+#include "../../core/include/alibabacloud/core/HttpClient.h"
+#include "../../core/src/CurlHttpClient.h"
+
+using namespace std;
+using ::testing::Return;
+using ::testing::DoAll;
+using ::testing::SetArgReferee;
+using ::testing::ReturnPointee;
+using ::testing::_;
+using ::testing::NiceMock;
+using ::testing::DefaultValue;
+
+using namespace AlibabaCloud;
+
+using namespace std;
+
+class mockCurlHttpClient : public CurlHttpClient {
+ public:
+  MOCK_METHOD1(makeRequest, HttpResponseOutcome (const HttpRequest &request));
+};
+
+// httpserver_for_ut port "echo text"
+// default port 8021
+// default text test/httpserver/index.html
+
+TEST(CurlHttpClient, basic) {
+  CurlHttpClient client;
+  HttpRequest request;
+
+  utUtils utils;
+  char dir[1024];
+  utils.get_dir_exec(dir, nullptr);
+  char httpserver_ut_with_args[10 * 1024];
+  const string testBody = "CurlHttpClient test boday";
+  snprintf(httpserver_ut_with_args, 10 * 1024, "%s/httpserver_for_ut  8021 \"%s\"", dir, testBody.c_str());
+
+  FILE* http = popen(httpserver_ut_with_args, "r");
+  EXPECT_TRUE(http != nullptr);
+
+  // wait util httpserver started
+  char buffer[100];
+  usleep(10 * 1000);
+  fgets(buffer, 100, http);
+
+  Url url;
+  url.setHost("127.0.0.1");
+  url.setPort(8021);
+
+  request.setMethod(HttpRequest::Method::Get);
+  request.setUrl(url);
+
+  request.setHeader("head1", "value1");
+  request.setHeader("head2", "value2");
+  HttpClient::HttpResponseOutcome out = client.makeRequest(request);
+  EXPECT_TRUE(out.result().body() == testBody);
+
+  request.setMethod(HttpRequest::Method::Put);
+  request.setUrl(url);
+
+  HttpClient::HttpResponseOutcome out1 = client.makeRequest(request);
+  EXPECT_TRUE(out1.result().body() == testBody);
+
+  request.setMethod(HttpRequest::Method::Post);
+  request.setUrl(url);
+
+  HttpClient::HttpResponseOutcome out2 = client.makeRequest(request);
+  EXPECT_TRUE(out2.result().body() == testBody);
+
+  pclose(http);
+}
+
+TEST(CurlHttpClient, netWorkError) {
+  CurlHttpClient client;
+  HttpRequest request;
+  HttpClient::HttpResponseOutcome out = client.makeRequest(request);
+  EXPECT_TRUE(out.error().errorCode() == "NetworkError");
+  EXPECT_TRUE(out.error().errorMessage() == "Failed to connect to host or proxy.");
+}
+
+TEST(CurlHttpClient, netWorkErrorWithHttpProxy) {
+  const std::string hostname = "hostname";
+  const std::string user = "user";
+  const std::string password = "password";
+  uint16_t port = 12345;
+  const NetworkProxy proxy(NetworkProxy::Http, hostname, port, user, password);
+
+  CurlHttpClient client;
+  client.setProxy(proxy);
+
+  HttpRequest request;
+  HttpClient::HttpResponseOutcome out = client.makeRequest(request);
+  EXPECT_TRUE(out.error().errorCode() == "NetworkError");
+  EXPECT_TRUE(out.error().errorMessage() == "Failed to connect to host or proxy.");
+}
+
+TEST(CurlHttpClient, netWorkErrorWithSocks5Proxy) {
+  const std::string hostname = "hostname";
+  const std::string user = "user";
+  const std::string password = "password";
+  uint16_t port = 12345;
+  const NetworkProxy proxy(NetworkProxy::Socks5, hostname, port, user, password);
+
+  CurlHttpClient client;
+  client.setProxy(proxy);
+
+  HttpRequest request;
+  HttpClient::HttpResponseOutcome out = client.makeRequest(request);
+  EXPECT_TRUE(out.error().errorCode() == "NetworkError");
+  EXPECT_TRUE(out.error().errorMessage() == "Failed to connect to host or proxy.");
+}
+
+TEST(CurlHttpClient, mock) {
+  CurlHttpClient client;
+  HttpRequest request;
+
+  string body = "1234567";
+  HttpResponse response(request);
+  response.setStatusCode(200);
+  response.setBody(body.c_str(), body.size());
+
+  NiceMock<mockCurlHttpClient> mclient;
+
+  HttpClient::HttpResponseOutcome res = HttpClient::HttpResponseOutcome(response);
+  HttpClient::HttpResponseOutcome out1;
+
+  out1.error().setErrorCode("111");
+  out1.error().setErrorMessage("this is a mock test");
+
+  DefaultValue<HttpClient::HttpResponseOutcome>::Set(res);
+
+  HttpRequest req;
+
+  EXPECT_CALL(mclient, makeRequest(_));
+
+  HttpClient::HttpResponseOutcome o = mclient.makeRequest(request);
+  EXPECT_TRUE(res.result().body() == body);
+}

+ 69 - 0
test/core/endpointprovider_ut.cc

@@ -0,0 +1,69 @@
+#include <iostream>
+#include <stdio.h>
+#include "gtest/gtest.h"
+#include "alibabacloud/core/EndpointProvider.h"
+
+using namespace std;
+using namespace AlibabaCloud;
+using namespace AlibabaCloud::Location;
+
+TEST(EndpointProvider, basic) {
+  ClientConfiguration configuration("cn-hangzhou");
+
+  char* key = getenv("ENV_AccessKeyId");
+  char* secret = getenv("ENV_AccessKeySecret");
+
+  bool no_key_provided = false;
+
+  string accessKeyId, accessKeySecret;
+  if (key == nullptr) {
+    accessKeyId = "no-AccessKeyId";
+    no_key_provided = true;
+  } else {
+    accessKeyId = string(key);
+  }
+  if (secret == nullptr) {
+    accessKeySecret = "no-AccessKeySecret";
+    no_key_provided = true;
+  } else {
+    accessKeySecret = string(secret);
+  }
+  auto locationClient = std::make_shared<LocationClient>(accessKeyId, accessKeySecret, configuration);
+
+  EndpointProvider ep(locationClient, configuration.regionId(), "Ecs", "ecs");
+  EndpointProvider::EndpointOutcome out = ep.getEndpoint();
+
+  if (no_key_provided) {
+    EXPECT_TRUE(out.error().errorCode() == "InvalidAccessKeyId.NotFound");
+  } else {
+    EXPECT_TRUE(out.result() == "ecs-cn-hangzhou.aliyuncs.com");
+  }
+}
+
+TEST(EndpointProvider, basic1) {
+  ClientConfiguration configuration("cn-hangzhou");
+
+  const string accessKeyId = "no-AccessKeyId";
+  const string accessKeySecret = "no-AccessKeySecret";
+
+  auto locationClient = std::make_shared<LocationClient>(accessKeyId, accessKeySecret, configuration);
+
+  EndpointProvider ep(locationClient, configuration.regionId(), "Ecs");
+  EndpointProvider::EndpointOutcome out = ep.getEndpoint();
+
+  EXPECT_TRUE(out.error().errorCode() == "InvalidRegionId");
+}
+
+
+TEST(EndpointProvider, basic2) {
+  ClientConfiguration configuration("xxxcn-hangzhou");
+
+  const string accessKeyId = "no-AccessKeyId";
+  const string accessKeySecret = "no-AccessKeySecret";
+
+  auto locationClient = std::make_shared<LocationClient>(accessKeyId, accessKeySecret, configuration);
+
+  EndpointProvider ep(locationClient, configuration.regionId(), "Ecs", "ecs");
+  EndpointProvider::EndpointOutcome out = ep.getEndpoint();
+  EXPECT_TRUE(out.error().errorCode() == "InvalidAccessKeyId.NotFound");
+}

+ 33 - 0
test/core/error_ut.cc

@@ -0,0 +1,33 @@
+#include "gtest/gtest.h"
+#include "alibabacloud/core/Error.h"
+
+using namespace std;
+using namespace AlibabaCloud;
+
+TEST(Error, basic) {
+  const std::string error_code = "errorCode";
+  const std::string error_message = "errorMessage";
+  const std::string host = "Host";
+  const std::string request_id = "requestId";
+
+  Error error(error_code, error_message);
+  EXPECT_TRUE(error.errorCode() == error_code);
+  EXPECT_TRUE(error.errorMessage() == error_message);
+  EXPECT_TRUE(error.host() == "");
+  EXPECT_TRUE(error.requestId() == "");
+
+  error.setHost(host);
+  error.setRequestId(request_id);
+  EXPECT_TRUE(error.errorCode() == error_code);
+  EXPECT_TRUE(error.errorMessage() == error_message);
+  EXPECT_TRUE(error.host() == host);
+  EXPECT_TRUE(error.requestId() == request_id);
+
+  const string code1 = "invalid regionId";
+  const string message1 = "invalid regionId cn-xxxx";
+  error.setErrorCode(code1);
+  error.setErrorMessage(message1);
+
+  EXPECT_TRUE(error.errorCode() == code1);
+  EXPECT_TRUE(error.errorMessage() == message1);
+}

+ 78 - 0
test/core/executor_ut.cc

@@ -0,0 +1,78 @@
+#include <iostream>
+#include <stdio.h>
+#include "gtest/gtest.h"
+#include "alibabacloud/core/Runnable.h"
+#include "../src/Executor.h"
+
+using namespace std;
+using namespace AlibabaCloud;
+
+namespace {
+
+static unsigned int nbr = 0;
+
+static void f0() {
+  usleep(3000);
+  nbr |= 0x01;
+}
+
+static void f1() {
+  usleep(3000);
+  nbr |= 0x02;
+}
+
+static void f2() {
+   usleep(3000);
+   nbr |= 0x04;
+}
+
+// there should be no exception
+  TEST(Executor, basic) {
+    std::function<void()> func0(f0);
+    std::function<void()> func1(f1);
+    std::function<void()> func2(f2);
+    Runnable* rf0 = new Runnable(func0);
+    Runnable* rf1 = new Runnable(func1);
+    Runnable* rf2 = new Runnable(func2);
+
+    Executor* e = new Executor;
+    EXPECT_TRUE(e->isShutdown() == true);
+
+    // thread created to execute tasks
+    e->start();
+
+    // before
+    EXPECT_TRUE(nbr == 0);
+
+    // push tasks to queue
+    e->instance()->execute(rf0);
+    e->instance()->execute(rf1);
+    e->instance()->execute(rf2);
+
+    // shutdown removes tasks in queue
+    e->shutdown();
+
+    // there is no time for task executing
+    // maybe some tasks executed as the async reason
+    EXPECT_TRUE(nbr != 0x07);
+
+    rf0 = new Runnable(func0);
+    rf1 = new Runnable(func1);
+    rf2 = new Runnable(func2);
+    nbr = 0;
+    e->execute(rf0);
+    usleep(5000);
+    // executor not started, nbr no changed
+    EXPECT_TRUE(nbr == 0);
+
+    e->start();
+    // there should be no more thread created
+    e->start();
+    e->instance()->execute(rf0);
+    e->execute(rf1);
+    e->execute(rf2);
+    usleep(20000);
+    EXPECT_TRUE(nbr == 0x07);
+    e->shutdown();
+  }
+}

+ 25 - 0
test/core/hmacsha1signer_ut.cc

@@ -0,0 +1,25 @@
+#include "gtest/gtest.h"
+#include "alibabacloud/core/HmacSha1Signer.h"
+#include <openssl/hmac.h>
+
+using namespace std;
+using namespace AlibabaCloud;
+
+TEST(HmacSha1Signer, assign) {
+
+  HmacSha1Signer sig;
+  string sign = sig.generate("GET&%2F&AccessKeyId%3Dtestid%26Action%3DDescribeRegions"
+      "%26Format%3DXML%26RegionId%3Dregion1%26SignatureMethod%3DHMAC-SHA1"
+      "%26SignatureNonce%3DNwDAxvLU6tFE0DVb%26SignatureVersion%3D1.0"
+      "%26TimeStamp%3D2012-12-26T10%253A33%253A56Z%26Version%3D2013-01-10",
+      "testsecret&");
+  EXPECT_TRUE("axE3FUHgDyfm9/+Iep0HpZXrRwE=" == sign);
+  EXPECT_TRUE(sig.type() == HmacSha1Signer::HmacSha1);
+  EXPECT_TRUE(sig.name() == "HMAC-SHA1");
+  EXPECT_TRUE(sig.version() == "1.0");
+}
+
+TEST(HmacSha1Signer, edge_from_empty_string) {
+  HmacSha1Signer sign;
+  EXPECT_TRUE(sign.generate("", "") == "");
+}

+ 56 - 0
test/core/httpmessage_ut.cc

@@ -0,0 +1,56 @@
+#include <iostream>
+#include <stdio.h>
+
+#include "gtest/gtest.h"
+#include "alibabacloud/core/HttpRequest.h"
+
+using namespace std;
+using namespace AlibabaCloud;
+
+TEST(HttpMessage, perRequest) {
+	const std::string data = "Thu, 19 Oct 2017 04:38:27 GMT";
+	HttpRequest req;
+	req.setHeader("accept", "application/xml");
+  EXPECT_TRUE(req.header("accept") == "application/xml");
+	req.setHeader("Accept", "application/json");
+  EXPECT_TRUE(req.header("accept") == "application/json");
+	EXPECT_TRUE(req.headers().size() == 1);
+  req.setHeader("Date", data);
+  EXPECT_TRUE(req.headers().size() == 2);
+	EXPECT_TRUE(req.header(HttpMessage::KnownHeader::Date) == data);
+
+  req.addHeader("header1", "value1");
+  EXPECT_TRUE(req.header("header1") == "value1");
+  EXPECT_TRUE(req.headers().size() == 3);
+
+  req.addHeader(HttpMessage::KnownHeader::ContentMD5, "testmd5sume");
+  EXPECT_TRUE(req.header(HttpMessage::KnownHeader::ContentMD5) == "testmd5sume");
+  EXPECT_TRUE(req.headers().size() == 4);
+
+  req.removeHeader("header1");
+  EXPECT_TRUE(req.header("header1") == "");
+  EXPECT_TRUE(req.headers().size() == 3);
+  req.removeHeader(HttpMessage::KnownHeader::ContentMD5);
+  EXPECT_TRUE(req.header(HttpMessage::KnownHeader::ContentMD5) == "");
+  EXPECT_TRUE(req.headers().size() == 2);
+
+  string body = "this is a test body";
+
+  req.setBody(body.c_str(), body.size());
+  EXPECT_TRUE(req.bodySize() == body.size());
+  EXPECT_TRUE(req.hasBody());
+  EXPECT_TRUE(req.body() != nullptr);
+
+
+}
+
+class TestHttpMessage: public HttpMessage {
+ public:
+  TestHttpMessage(): HttpMessage(){}
+};
+
+TEST(HttpMessage, A) {
+  TestHttpMessage tm;
+  TestHttpMessage ttm(tm);
+
+}

+ 30 - 0
test/core/httprequest_ut.cc

@@ -0,0 +1,30 @@
+#include <iostream>
+#include <stdio.h>
+#include "gtest/gtest.h"
+#include "alibabacloud/core/HttpRequest.h"
+
+using namespace std;
+using namespace AlibabaCloud;
+
+TEST(HttpRequest, basic) {
+  const string src = "abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1";
+  Url url;
+  url.setScheme("abc");
+  url.setUserName("username");
+  url.setPassword("password");
+  url.setHost("example.com");
+  url.setPath("/path/data");
+  url.setPort(123);
+  url.setQuery("key=value&key2=value2");
+  url.setFragment("fragid1");
+
+  HttpRequest http_request(url, HttpRequest::Method::Post);
+
+  EXPECT_TRUE(http_request.method() == HttpRequest::Method::Post);
+  http_request.setMethod(HttpRequest::Method::Get);
+  EXPECT_TRUE(http_request.method() == HttpRequest::Method::Get);
+
+  http_request.setUrl(url);
+  Url rUrl = http_request.url();
+  EXPECT_TRUE(rUrl.toString() == src);
+}

+ 18 - 0
test/core/httpresponse_ut.cc

@@ -0,0 +1,18 @@
+#include "gtest/gtest.h"
+#include "alibabacloud/core/HttpResponse.h"
+
+using namespace std;
+using namespace AlibabaCloud;
+
+TEST(HttpResponse, basic) {
+  HttpResponse r;
+  r.setStatusCode(202);
+  EXPECT_TRUE(r.statusCode() == 202);
+
+  Url url;
+  url.setHost("example.com");
+  HttpRequest req(url, HttpRequest::Method::Post);
+  HttpResponse res(req);
+
+  EXPECT_TRUE(res.request().url().host() == "example.com");
+}

+ 21 - 0
test/core/networkproxy_ut.cc

@@ -0,0 +1,21 @@
+#include "gtest/gtest.h"
+#include "alibabacloud/core/NetworkProxy.h"
+
+using namespace std;
+using namespace AlibabaCloud;
+
+TEST(NetworProxyTest, basic) {
+  NetworkProxy proxy(NetworkProxy::Http);
+  proxy.setHostName("abc.com");
+  proxy.setPassword("myPassword");
+  proxy.setPort(123);
+  proxy.setUser("tester");
+
+  EXPECT_TRUE(proxy.type() == NetworkProxy::Http);
+  proxy.setType(NetworkProxy::Socks5);
+  EXPECT_TRUE(proxy.type() == NetworkProxy::Socks5);
+  EXPECT_TRUE(proxy.hostName() == "abc.com");
+  EXPECT_TRUE(proxy.password() == "myPassword");
+  EXPECT_TRUE(proxy.user() == "tester");
+  EXPECT_TRUE(proxy.port() == 123);
+}

+ 16 - 0
test/core/roaservicerequest_ut.cc

@@ -0,0 +1,16 @@
+#include "gtest/gtest.h"
+#include "alibabacloud/core/RoaServiceRequest.h"
+
+
+using namespace std;
+using namespace AlibabaCloud;
+
+TEST(test_RoaServiceRequest, basic) {
+  const string product = "ECS";
+  const string version = "1.0";
+
+  RoaServiceRequest roa(product, version);
+  EXPECT_TRUE(roa.version() == version);
+  EXPECT_TRUE(roa.product() == product);
+}
+

+ 18 - 0
test/core/rpcservicerequest_ut.cc

@@ -0,0 +1,18 @@
+#include <iostream>
+#include <stdio.h>
+#include "gtest/gtest.h"
+#include "alibabacloud/core/RpcServiceRequest.h"
+
+using namespace std;
+using namespace AlibabaCloud;
+
+TEST(test_RpcServiceRequest, basic) {
+  const string product = "ECS";
+  const string version = "1.0";
+  const string action = "testAction";
+  RpcServiceRequest rpc(product, version, action);
+
+  EXPECT_TRUE(rpc.actionName() == action);
+  EXPECT_TRUE(rpc.product() == product);
+  EXPECT_TRUE(rpc.version() == version);
+}

+ 22 - 0
test/core/runnable_ut.cc

@@ -0,0 +1,22 @@
+#include <iostream>
+#include <stdio.h>
+#include "gtest/gtest.h"
+#include "alibabacloud/core/Runnable.h"
+
+using namespace std;
+using namespace AlibabaCloud;
+
+
+static int nbr = 0;
+void foo() {
+  nbr = 1;
+}
+
+// there should be no exception
+TEST(Runnable, basic) {
+  std::function<void()> func(foo);
+  Runnable f(func);
+  EXPECT_TRUE(nbr == 0);
+  f.run();
+  EXPECT_TRUE(nbr == 1);
+}

+ 110 - 0
test/core/servicerequest_ut.cc

@@ -0,0 +1,110 @@
+
+#include <iostream>
+#include <stdio.h>
+#include <string.h>
+#include "gtest/gtest.h"
+#include "alibabacloud/core/ServiceRequest.h"
+
+using namespace std;
+using namespace AlibabaCloud;
+
+namespace AlibabaCloud{
+  class TestServiceRequest: public ServiceRequest {
+   public:
+    explicit TestServiceRequest(const std::string &product, const std::string &version):
+      ServiceRequest(product, version)
+    {}
+    explicit TestServiceRequest(const ServiceRequest &other):
+      ServiceRequest(other)
+    {}
+    explicit TestServiceRequest(ServiceRequest &&other):
+      ServiceRequest(other)
+    {}
+
+
+    void addParameter(const ParameterNameType &k, const ParameterValueType &v) {
+      ServiceRequest::addParameter(k, v);
+    }
+
+    void removeParameter(const ParameterNameType &k) {
+      ServiceRequest::removeParameter(k);
+    }
+
+    ServiceRequest::ParameterValueType parameter (const ParameterNameType &name) {
+      return ServiceRequest::parameter(name);
+    }
+
+    void setContent(const char* data, size_t size) {
+      ServiceRequest::setContent(data, size);
+    }
+    void setParameter(const ParameterNameType &k, const ParameterValueType &v) {
+      ServiceRequest::setParameter(k, v);
+    }
+    void setParameters(const ParameterCollection &params) {
+      ServiceRequest::setParameters(params);
+    }
+
+    void setResourcePath (const std::string &p) {
+      ServiceRequest::setResourcePath(p);
+    }
+
+    void setProduct (const std::string &product) {
+      ServiceRequest::setProduct(product);
+    }
+
+    void setVersion(const std::string &version) {
+      ServiceRequest::setVersion(version);
+    }
+  };
+}
+
+TEST(ServiceRequestTest, basic) {
+
+  TestServiceRequest sr1("ECS", "1.0");
+  TestServiceRequest sr2(sr1);
+
+  sr1.addParameter("k1", "v1");
+  sr2.addParameter("k2", "v2");
+
+  ServiceRequest::ParameterCollection p1 = sr1.parameters();
+  ServiceRequest::ParameterCollection p2 = sr2.parameters();
+
+  EXPECT_TRUE(sr1.parameter("k1") == "v1");
+  sr1.addParameter("kx", "vx");
+  EXPECT_TRUE(sr1.parameter("kx") == "vx");
+
+  EXPECT_TRUE(sr1.parameters().size() == 2);
+
+  sr1.removeParameter("k1");
+  EXPECT_TRUE(sr1.parameters().size() == 1);
+
+  sr1.setParameter("ka", "va");
+  EXPECT_TRUE(sr1.parameter("ka") == "va");
+
+  sr1.setContent("123456", 6);
+  EXPECT_TRUE(sr1.contentSize() == 6);
+  EXPECT_TRUE(sr1.hasContent() == true);
+  EXPECT_TRUE(strncmp(sr1.content(), "123456", 6) == 0);
+
+  sr1.setContent("aliyunsdk", 9);
+  EXPECT_TRUE(sr1.contentSize() == 9);
+  EXPECT_TRUE(sr1.hasContent() == true);
+  EXPECT_TRUE(strncmp(sr1.content(), "aliyunsdk", 9) == 0);
+
+  sr1.setProduct("OSS");
+  EXPECT_TRUE(sr1.product() == "OSS");
+
+  sr1.setVersion("3.0");
+  EXPECT_TRUE(sr1.version() == "3.0");
+
+  sr1.setResourcePath("http://abc.com/a/b/c");
+  EXPECT_TRUE(sr1.resourcePath() == "http://abc.com/a/b/c");
+
+  ServiceRequest::ParameterCollection ps;
+  ps["km"] = "vm";
+  ps["kn"] = "vn";
+  sr1.setParameters(ps);
+  ServiceRequest::ParameterCollection pc = sr1.parameters();
+  EXPECT_TRUE(pc.at("km") == "vm");
+  EXPECT_TRUE(pc.at("kn") == "vn");
+}

+ 29 - 0
test/core/serviceresult_ut.cc

@@ -0,0 +1,29 @@
+#include <iostream>
+#include <stdio.h>
+#include "gtest/gtest.h"
+#include "alibabacloud/core/ServiceResult.h"
+
+
+using namespace std;
+using namespace AlibabaCloud;
+
+namespace AlibabaCloud{
+  class myServiceResult: public ServiceResult {
+   public:
+    myServiceResult(){}
+
+    void setRequestId(const string request_id) {
+      ServiceResult::setRequestId(request_id);
+    }
+  };
+}
+
+TEST(ServiceResult, basic) {
+  ServiceResult* service_result = new ServiceResult();
+  EXPECT_TRUE(service_result->requestId() == "");
+  myServiceResult* sr = new myServiceResult();
+  sr->setRequestId("testRequestId");
+  EXPECT_TRUE(sr->requestId() == "testRequestId");
+  delete(service_result);
+  delete(sr);
+}

+ 20 - 0
test/core/signer_ut.cc

@@ -0,0 +1,20 @@
+
+#include "gtest/gtest.h"
+#include "alibabacloud/core/Signer.h"
+
+using namespace std;
+using namespace AlibabaCloud;
+
+namespace {
+  TEST(Signer, basic) {
+
+    HmacSha1Signer sig;
+    string sign = sig.generate("GET&%2F&AccessKeyId%3Dtestid%26Action%3DDescribeRegions"
+        "%26Format%3DXML%26RegionId%3Dregion1%26SignatureMethod%3DHMAC-SHA1"
+        "%26SignatureNonce%3DNwDAxvLU6tFE0DVb%26SignatureVersion%3D1.0"
+        "%26TimeStamp%3D2012-12-26T10%253A33%253A56Z%26Version%3D2013-01-10",
+        "testsecret&");
+    EXPECT_TRUE("axE3FUHgDyfm9/+Iep0HpZXrRwE=" == sign);
+
+  }
+}

+ 23 - 0
test/core/simplecredentialsprovider_ut.cc

@@ -0,0 +1,23 @@
+#include "gtest/gtest.h"
+#include "alibabacloud/core/Credentials.h"
+#include "alibabacloud/core/sts/StsClient.h"
+#include "alibabacloud/core/SimpleCredentialsProvider.h"
+
+using namespace std;
+using namespace AlibabaCloud;
+
+TEST(SimpleCredentialsProvider, basic) {
+
+  const string access_key = "accessKeyId";
+  const string access_secret = "accessKeySecret";
+  Credentials credentials(access_key, access_secret);
+
+  SimpleCredentialsProvider p1(credentials);
+  SimpleCredentialsProvider p2(access_key, access_secret);
+
+  EXPECT_TRUE(p1.getCredentials().accessKeyId() == access_key);
+  EXPECT_TRUE(p1.getCredentials().accessKeySecret() == access_secret);
+
+  EXPECT_TRUE(p2.getCredentials().accessKeyId() == access_key);
+  EXPECT_TRUE(p2.getCredentials().accessKeySecret() == access_secret);
+}

+ 118 - 0
test/core/url_ut.cc

@@ -0,0 +1,118 @@
+#include "gtest/gtest.h"
+#include "alibabacloud/core/Url.h"
+
+using namespace std;
+using namespace AlibabaCloud;
+
+namespace {
+  TEST(UrlTest, assign) {
+    const string src = "abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1";
+    Url url;
+    url.setScheme("abc");
+    url.setUserName("username");
+    url.setPassword("password");
+    url.setHost("example.com");
+    url.setPath("/path/data");
+    url.setPort(123);
+    url.setQuery("key=value&key2=value2");
+    url.setFragment("fragid1");
+    EXPECT_TRUE(url.toString() == src);
+
+    EXPECT_TRUE(url.scheme() == "abc");
+    EXPECT_TRUE(url.userName() == "username");
+    EXPECT_TRUE(url.password() == "password");
+    EXPECT_TRUE(url.host() == "example.com");
+    EXPECT_TRUE(url.path() == "/path/data");
+    EXPECT_TRUE(url.port() == 123);
+    EXPECT_TRUE(url.query() == "key=value&key2=value2");
+    EXPECT_TRUE(url.fragment() == "fragid1");
+
+    url.clear();
+    EXPECT_TRUE(url.toString() == "");
+    EXPECT_TRUE(url.scheme() == "");
+    EXPECT_TRUE(url.userName() == "");
+    EXPECT_TRUE(url.password() == "");
+    EXPECT_TRUE(url.host() == "");
+    EXPECT_TRUE(url.path() == "");
+    EXPECT_TRUE(url.port() == -1);
+    EXPECT_TRUE(url.query() == "");
+    EXPECT_TRUE(url.fragment() == "");
+
+    url.fromString(src);
+
+    EXPECT_TRUE(url.scheme() == "abc");
+    EXPECT_TRUE(url.userName() == "username");
+    EXPECT_TRUE(url.password() == "password");
+    EXPECT_TRUE(url.host() == "example.com");
+    EXPECT_TRUE(url.path() == "/path/data");
+    EXPECT_TRUE(url.port() == 123);
+    EXPECT_TRUE(url.query() == "key=value&key2=value2");
+    EXPECT_TRUE(url.fragment() == "fragid1");
+
+    Url newurl = url;
+    EXPECT_TRUE(newurl.scheme() == "abc");
+    EXPECT_TRUE(newurl.userName() == "username");
+    EXPECT_TRUE(newurl.password() == "password");
+    EXPECT_TRUE(newurl.host() == "example.com");
+    EXPECT_TRUE(newurl.path() == "/path/data");
+    EXPECT_TRUE(newurl.port() == 123);
+    EXPECT_TRUE(newurl.query() == "key=value&key2=value2");
+    EXPECT_TRUE(newurl.fragment() == "fragid1");
+  }
+
+  TEST(UrlTest, from_string) {
+    const string src = "http://oss.example.com";
+    Url url(src);
+    EXPECT_TRUE(url.scheme() == "http");
+    EXPECT_TRUE(url.userName() == "");
+    EXPECT_TRUE(url.password() == "");
+    EXPECT_TRUE(url.host() == "oss.example.com");
+    EXPECT_TRUE(url.path() == "/");
+    EXPECT_TRUE(url.port() == -1);
+    EXPECT_TRUE(url.query() == "");
+    EXPECT_TRUE(url.fragment() == "");
+  }
+
+  TEST(UrlTest, abnormal) {
+    const string src = "http://oss.example.com";
+    Url url(src);
+    Url newurl = url;
+    newurl.setPort(12345);
+    EXPECT_TRUE(url.isEmpty() == false);
+    EXPECT_TRUE(url.isValid());
+    EXPECT_TRUE(newurl != url);
+    EXPECT_TRUE(newurl.port() == 12345);
+  }
+
+  TEST(UrlTest, abnormal_2) {
+    const string src = "";
+    Url empty_url;
+    empty_url.fromString(src);
+    EXPECT_TRUE(empty_url.isEmpty());
+    Url url(src);
+    Url newurl = url;
+    EXPECT_TRUE(url == newurl);
+
+    newurl.setPort(12345);
+
+    EXPECT_TRUE(url.isEmpty() == true);
+    EXPECT_TRUE(url.isValid() == false);
+    EXPECT_TRUE(url.authority() == "");
+    EXPECT_TRUE(newurl != url);
+    EXPECT_TRUE(newurl.port() == 12345);
+    newurl.setAuthority("");
+    EXPECT_TRUE(newurl.host() == "");
+    EXPECT_TRUE(newurl.port() == -1);
+    EXPECT_TRUE(newurl.userInfo() == "");
+
+    newurl.setScheme("");
+    EXPECT_TRUE(newurl.scheme() == "");
+
+    newurl.setUserInfo("userinfo");
+    EXPECT_TRUE(newurl.userName() == "userinfo");
+
+    newurl.setUserInfo("user:password");
+    EXPECT_TRUE(newurl.userName() == "user");
+    EXPECT_TRUE(newurl.password() == "password");
+  }
+}

+ 30 - 0
test/core/utils.h

@@ -0,0 +1,30 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+
+class utUtils {
+ public:
+  void get_dir_exec(char* dir, char* exec) {
+    char* filename = nullptr;
+    if (readlink("/proc/self/exe", dir, 1024) < 0) {
+      dir[0] = '\0';
+      return;
+    }
+    filename = strrchr(dir, '/');
+    if (filename == nullptr) {
+      dir[0] = '\0';
+      return;
+    }
+    ++filename;
+    if (exec) {
+      sprintf(exec, "%s", filename);
+    }
+
+    *filename = '\0';
+    return;
+  }
+};
+
+
+

+ 63 - 0
test/core/utils_ut.cc

@@ -0,0 +1,63 @@
+#include "gtest/gtest.h"
+#include "../../core/src/Utils.h"
+
+
+using namespace std;
+using namespace AlibabaCloud;
+
+namespace {
+  TEST(UtilsTest, ComputeContentMD5) {
+    const std::string src = "abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1";
+    const std::string md5 = ComputeContentMD5(src.c_str(), src.size());
+    EXPECT_TRUE(md5 == "WL7+fwrxHSlBTsfU9WCSbA==");
+  }
+
+  TEST(UtilsTest, GenerateUuid) {
+    const std::string uuid1 = GenerateUuid();
+    const std::string uuid2 = GenerateUuid();
+    EXPECT_TRUE(uuid1 != uuid2);
+  }
+
+  TEST(UtilsTest, HttpMethodToString){
+    std::string method = HttpMethodToString(HttpRequest::Method::Head);
+    EXPECT_TRUE(method == "HEAD");
+    method = HttpMethodToString(HttpRequest::Method::Post);
+    EXPECT_TRUE(method == "POST");
+    method = HttpMethodToString(HttpRequest::Method::Put);
+    EXPECT_TRUE(method == "PUT");
+    method = HttpMethodToString(HttpRequest::Method::Delete);
+    EXPECT_TRUE(method == "DELETE");
+    method = HttpMethodToString(HttpRequest::Method::Connect);
+    EXPECT_TRUE(method == "CONNECT");
+    method = HttpMethodToString(HttpRequest::Method::Options);
+    EXPECT_TRUE(method == "OPTIONS");
+    method = HttpMethodToString(HttpRequest::Method::Patch);
+    EXPECT_TRUE(method == "PATCH");
+    method = HttpMethodToString(HttpRequest::Method::Trace);
+    EXPECT_TRUE(method == "TRACE");
+    method = HttpMethodToString(HttpRequest::Method::Get);
+    EXPECT_TRUE(method == "GET");
+  }
+
+  TEST(UtilsTest, StringReplace) {
+    std::string src = "abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1";
+    StringReplace(src, "abc", "http");
+    EXPECT_TRUE(src == "http://username:password@example.com:123/path/data?key=value&key2=value2#fragid1");
+    StringReplace(src, "value", "VALUE");
+    EXPECT_TRUE(src == "http://username:password@example.com:123/path/data?key=VALUE&key2=VALUE2#fragid1");
+  }
+
+  TEST(UtilsTest, Encode) {
+    const std::string url = "http://username:password@example.com:123/path/data?key=value&key2=value2#fragid1";
+    const std::string encoded = "http%3A%2F%2Fusername%3Apassword%40example.com%3A123%2Fpath%2Fdata%3Fkey%3Dvalue%26key2%3Dvalue2%23fragid1";
+    const std::string enc = UrlEncode(url);
+    EXPECT_TRUE(enc == encoded);
+  }
+
+  TEST(UtilsTest, Decode) {
+    const std::string url = "http://username:password@example.com:123/path/data?key=value&key2=value2#fragid1";
+    const std::string encoded = "http%3A%2F%2Fusername%3Apassword%40example.com%3A123%2Fpath%2Fdata%3Fkey%3Dvalue%26key2%3Dvalue2%23fragid1";
+    const std::string decoded = UrlDecode(encoded);
+    EXPECT_TRUE(decoded == url);
+  }
+}

+ 21 - 0
test/httpserver/CMakeLists.txt

@@ -0,0 +1,21 @@
+cmake_minimum_required(VERSION 3.0)
+set(CMAKE_CXX_STANDARD 11)
+
+add_executable(httpserver_for_ut
+  HTTPServer.cpp
+  ServerThread.cpp
+  main.cpp
+)
+
+# httpserver_for_ut use pthread
+find_package(Threads)
+target_link_libraries(httpserver_for_ut ${CMAKE_THREAD_LIBS_INIT})
+
+# copy httpserver_for_ut to the same directory as the core_ut binary
+set_target_properties(httpserver_for_ut
+  PROPERTIES
+  OUTPUT_NAME httpserver_for_ut
+  RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
+
+# httpserver_for_ut echo this message to client
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/index.html ${CMAKE_BINARY_DIR}/bin/index.html COPYONLY)

+ 118 - 0
test/httpserver/HTTPServer.cpp

@@ -0,0 +1,118 @@
+#include "HTTPServer.h"
+
+using namespace std;
+
+// the terminator logic, run in a separate thread
+
+void *tfunc(void* obj) {
+  ((HTTPServer*) obj)->RunTerminator();
+}
+
+void* sfunc(void* obj) {
+  ((HTTPServer*) obj)->RunServer();
+}
+
+void error(string msg) {
+  cerr << msg << endl;
+  exit(1);
+}
+
+void msg(string msg) {
+  cout << msg << endl;
+}
+
+HTTPServer::HTTPServer(int port) {
+  this->portno = port;
+}
+
+HTTPServer::~HTTPServer() {
+}
+
+void HTTPServer::StartServer() {
+  // socket(int domain, int type, int protocol)
+  sockfd = socket(AF_INET, SOCK_STREAM, 0);
+  if (sockfd < 0)
+    error("Opening socket");
+
+  // clear the struct's memory
+  bzero((char *) &serv_addr, sizeof (serv_addr));
+
+  // setup the host_addr structure
+  serv_addr.sin_family = AF_INET;
+
+  // automatically be filled with current host's IP address which is localhost
+  serv_addr.sin_addr.s_addr = INADDR_ANY;
+
+  // convert short to network port order
+  serv_addr.sin_port = htons(portno);
+
+  // to avoid waiting for limbo connection when debugging
+  int en = 1;
+  setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &en, sizeof (int));
+
+  // bind(int fd, struct sockaddr *local_addr, socklen_t addr_length)
+  // This bind() call will bind  the socket to the current IP address on our port
+  if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof (serv_addr)) < 0)
+    error("On binding");
+  // This listen() function will start filling the backlog queue with clients
+  // The maximum size for the backlog queue to 1
+  listen(sockfd, 1);
+
+  clilen = sizeof (cli_addr);
+
+  // create the server thread
+  pthread_create(&server, NULL, sfunc, this);
+}
+
+void HTTPServer::StartTerminator() {
+  // create the terminator thread
+  pthread_create(&terminator, NULL, tfunc, this);
+}
+
+void HTTPServer::RunServer() {
+  msg("Server is online!");
+  while (true) {
+    // The accept() function blocks untill the backlog has elements
+    // It then stores the info of the client socket in newsockfd
+    int csockn = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
+    if (csockn < 0)
+      msg("Failed on accept.");
+
+    printf("Incoming connection from %s port %ho\n",
+        inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
+    ServerThread* clt = new ServerThread(csockn);
+    clt->Serve();
+    aclients.push_front(clt);
+  }
+}
+
+void HTTPServer::RunTerminator() {
+  while (true) {
+    sleep(0.01);
+    time_t now = time(NULL);
+    int nc = aclients.size();
+    int ttl = 1 / (double) (nc > 0 ? nc : 1); //in seconds
+    for (list<ServerThread*>::iterator list_iter = aclients.begin(); list_iter != aclients.end();) {
+      ServerThread* st = *list_iter;
+      int runningtime = difftime(now, st->GetStartTime());
+      if (st->IsMarked() || (runningtime >= ttl && st->Terminate(false))) {
+        if (st->IsMarked())
+          cout << "Client on socket " << st->GetSocket() << " self terminated" << endl;
+        else
+          cout << "Terminated client on socket " << st->GetSocket() << endl;
+        delete st;
+        list_iter = aclients.erase(list_iter);
+      } else {
+        list_iter++;
+      }
+    }
+  }
+}
+
+void HTTPServer::Stop() {
+  close(sockfd);
+  if (server)
+    pthread_cancel(server);
+  if (terminator)
+    pthread_cancel(terminator);
+}

+ 52 - 0
test/httpserver/HTTPServer.h

@@ -0,0 +1,52 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/*
+ * File:   HTTPServer.h
+ * Author: root
+ *
+ * Created on November 5, 2016, 5:46 PM
+ */
+
+#ifndef HTTPSERVER_H
+#define HTTPSERVER_H
+#include <list>
+#include "ServerThread.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string>
+#include <iostream>
+
+using namespace std;
+
+class HTTPServer {
+public:
+  HTTPServer(int port);
+  HTTPServer(const HTTPServer& orig);
+  virtual ~HTTPServer();
+  void RunServer();
+  void RunTerminator();
+  void StartServer();
+  void StartTerminator();
+  void Stop();
+private:
+  pthread_t server, terminator;
+  // a list of active clients
+  list<ServerThread*> aclients;
+  // socket fields
+  int sockfd, portno;
+  sockaddr_in serv_addr, cli_addr;
+  socklen_t clilen;
+};
+
+#endif /* HTTPSERVER_H */
+

+ 163 - 0
test/httpserver/ServerThread.cpp

@@ -0,0 +1,163 @@
+#include "ServerThread.h"
+
+using namespace std;
+
+// entry point for all threads
+char echo_buffer[100 *1024];
+
+void getExecDir(char*dir) {
+  char* filename = nullptr;
+  if (readlink("/proc/self/exe", dir, 1024) < 0) {
+    dir[0] = '\0';
+    return;
+  }
+
+  filename = strrchr(dir, '/');
+  if (filename == nullptr) {
+    dir[0] = '\0';
+    return;
+  }
+  ++filename;
+  *filename = '\0';
+  sprintf(dir + strlen(dir), "%s", "index.html");
+  return;
+}
+
+void* run(void* obj) {
+  ((ServerThread*) obj)->Run();
+  // when Run() finishes, mark for termination
+  ((ServerThread*) obj)->MarkFT();
+}
+
+bool getExt(const char* path, char* ext) {
+  int l = strlen(path), i = l;
+  while (i > 0 && path[--i] != '.' && path[i] != '/');
+  if (i == 0 || path[i] == '/') return false;
+  for (int j = 0; i < l; ext[j++] = path[++i]);
+  return true;
+}
+
+int getHeader(char *header, const char *buffer, int rd) {
+  int hl = 0;
+  int i = 0;
+  for (; hl < rd && strcmp(term, buffer + hl); hl++) {
+    for (i = 0; i < 4; i++)
+      if (term[i] != buffer[hl + i]) break;
+    if (i == 4) break;
+    header[hl] = buffer[hl];
+  }
+  header[hl] = '\0';
+  return hl;
+}
+///////////////////////////////////////////////////////////
+
+ServerThread::ServerThread(int socket) {
+  this->socket = socket;
+  marked = serving = false;
+}
+
+ServerThread::~ServerThread() {
+}
+// start serving in a separate thread
+
+void ServerThread::Serve() {
+  pthread_create(&tid, NULL, run, this);
+  start = time(NULL);
+}
+// terminate the thread and close the socket
+
+bool ServerThread::Terminate(bool force) {
+  if (serving&&!force)return false;
+  pthread_cancel(tid);
+  close(socket);
+  return true;
+}
+// run in current thread
+
+void ServerThread::Run() {
+  int rd, hl;
+  while (1) {
+    serving = false;
+    rd = read(socket, buffer, BUFFERLENGTH - 1);
+    if (rd <= 0) {
+      // client disconnected mysteriously
+      Terminate(true);
+      break; // precautionary
+    }
+    // parse the header
+    serving = true;
+    char method[8], path[261], version[8], ext[5];
+    strcpy(path, WORKDIR);
+    // getExecDir(path);
+    hl = getHeader(header, buffer, rd);
+    sscanf(header, "%s %s %s", method, path + WDLEN, version);
+    // handle default file name
+    // if (!getExt(path, ext))strcat(path, "/index.html");
+    getExecDir(path);
+
+    // handle the request
+    if (!strcmp("GET", method) || !strcmp("PUT", method)) {
+
+      if (strlen(echo_buffer) > 0) {
+        int size = strlen(echo_buffer);
+        sprintf(buffer, "%s%d\r\n\r\n", "HTTP/1.1 200 OK\r\nContent-Length: ", size);
+        send(socket, buffer, strlen(buffer), 0);
+        send(socket, echo_buffer, size, 0);
+      } else {
+        // try to open the requested file
+        FILE * file = fopen(path, "r");
+        if (file) {
+          fseek(file, 0, SEEK_END);
+          int size = ftell(file);
+          fseek(file, 0, 0);
+          // send 200
+          sprintf(buffer, "%s%d\r\n\r\n", "HTTP/1.1 200 OK\r\nContent-Length: ", size);
+          send(socket, buffer, strlen(buffer), 0);
+          cout << "200 " << path << endl;
+          do {
+            rd = fread(buffer, sizeof (char), BUFFERLENGTH, file);
+            send(socket, buffer, rd, 0);
+          } while (rd > 0);
+          fclose(file);
+        } else {
+          // send 404
+          sprintf(buffer, "%s\r\n\r\n", "HTTP/1.1 404 Not Found");
+          send(socket, buffer, strlen(buffer), 0);
+          cout << "404 " << path << endl;
+        }
+      }
+      serving = false;
+      Terminate(true);
+    } else if (!strcmp("POST", method)) {
+      if (fopen(path, "r") > 0) {
+        sprintf(buffer, "%s\r\n\r\n", "HTTP/1.1 403 Forbidden");
+        send(socket, buffer, strlen(buffer), 0);
+        cout << "402 " << path << endl;
+        continue;
+      }
+      FILE *file = fopen(path, "w+");
+      if (file) {
+        // send 200
+        sprintf(buffer, "%s\r\n\r\n", "HTTP/1.1 200 OK");
+        send(socket, buffer, strlen(buffer), 0);
+        cout << "200 " << path << endl;
+        // if client sent data after the header
+        if (rd - hl > 4)
+          fwrite(buffer + hl + 4, sizeof (char), rd - hl - 4, file);
+        rd = read(socket, buffer, BUFFERLENGTH - 1);
+        while (rd > 0) {
+          fwrite(buffer, sizeof (char), rd, file);
+          rd = read(socket, buffer, BUFFERLENGTH - 1);
+          // TODO: implement blocks or read content length
+          if (rd < BUFFERLENGTH - 1 || !strcmp(buffer - 4, term))
+            break;
+        }
+        fclose(file);
+      } else {
+        sprintf(buffer, "%s\r\n\r\n", "HTTP/1.1 500 Internal Server Error");
+        send(socket, buffer, strlen(buffer), 0);
+        cout << "500 " << path << endl;
+      }
+    }
+  }
+}

+ 55 - 0
test/httpserver/ServerThread.h

@@ -0,0 +1,55 @@
+#ifndef SERVERTHREAD_H
+#define SERVERTHREAD_H
+
+#include <ctime>
+#include <pthread.h>
+#include <iostream>
+#include <ctime>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <string.h>
+
+#define BUFFERLENGTH 8192
+#define WORKDIR "/tmp"
+#define WDLEN 11
+#define term "\r\n\r\n"
+
+class ServerThread {
+private:
+  int socket;
+  pthread_t tid;
+  std::time_t start;
+  char buffer[BUFFERLENGTH];
+  char header[BUFFERLENGTH];
+  bool marked;
+  bool serving;
+public:
+  ServerThread(int socket);
+  virtual ~ServerThread();
+  void Serve();
+  void Run();
+  bool Terminate(bool force);
+
+  int GetSocket() {
+    return this->socket;
+  }
+
+  time_t GetStartTime() {
+    return this->start;
+  }
+
+  pthread_t GetThreadID() {
+    return this->tid;
+  }
+
+  void MarkFT() {
+    marked = true;
+  }
+
+  bool IsMarked() {
+    return marked;
+  }
+};
+
+#endif // SERVERTHREAD_H

+ 1 - 0
test/httpserver/index.html

@@ -0,0 +1 @@
+{"Endpoints":{"Endpoint":[{"Protocols":{"Protocols":["HTTP","HTTPS"]},"Type":"openAPI","Namespace":"26842","Id":"cn-hangzhou","SerivceCode":"ecs","Endpoint":"ecs-cn-hangzhou.aliyuncs.com"}]},"RequestId":"9AF4C364-08A0-4C00-A002-9E4DF57E3389","Success":true}

+ 67 - 0
test/httpserver/main.cpp

@@ -0,0 +1,67 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <iostream>
+#include "ServerThread.h"
+#include "HTTPServer.h"
+#include <pthread.h>
+
+using namespace std;
+
+extern char echo_buffer[100 *1024];
+
+// command symbol table
+
+enum _cmd {
+  cunknown,
+  cexit
+};
+
+_cmd str2cmd(string cmdstr) {
+  if (cmdstr == "x" || cmdstr == "exit" || cmdstr == "quit")
+    return cexit;
+  return cunknown;
+}
+
+int main(int argc, char *argv[]) {
+  string cmd;
+  bool running = true;
+  int portno = 8021;
+  // the first argument is always the work directory.
+  if (argc == 2) {
+    // get port number from arguments
+    portno = atoi(argv[1]);
+    echo_buffer[0] = '\0';
+  }
+  if (argc >= 3) {
+    // get port number from arguments
+    portno = atoi(argv[1]);
+    snprintf(echo_buffer, sizeof(echo_buffer), "%s", argv[2]);
+  }
+
+
+  HTTPServer* server = new HTTPServer(portno);
+  server->StartServer();
+  // without the terminator, only the client can close the connection
+  server->StartTerminator();
+  while (running) {
+    cin >> cmd;
+    if (cmd != "")
+      switch (str2cmd(cmd)) {
+        case cexit:
+          running = false;
+          break;
+        default:
+          cout << "Unrecognized command." << endl;
+          break;
+      }
+  }
+  server->Stop();
+  return 0;
+}
+