support http put and delete
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -7,3 +7,7 @@ Testing/
|
|||||||
ft_build/
|
ft_build/
|
||||||
ut_build/
|
ut_build/
|
||||||
sdk_build/
|
sdk_build/
|
||||||
|
test/httpserver/node_modules
|
||||||
|
test/httpserver/package-lock.json
|
||||||
|
test/httpserver/nohup.out
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ language: cpp
|
|||||||
compiler: gcc
|
compiler: gcc
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- sudo apt-get install lcov libcurl4-openssl-dev libssl-dev uuid-dev libjsoncpp-dev
|
- sudo apt-get install lcov libcurl4-openssl-dev libssl-dev uuid-dev libjsoncpp-dev nodejs npm
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- ./unit_test.sh
|
- ./unit_test.sh
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ add_subdirectory(core)
|
|||||||
if(BUILD_UNIT_TESTS)
|
if(BUILD_UNIT_TESTS)
|
||||||
enable_testing()
|
enable_testing()
|
||||||
add_subdirectory(test/core)
|
add_subdirectory(test/core)
|
||||||
add_subdirectory(test/httpserver)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(BUILD_FUNCTION_TESTS)
|
if(BUILD_FUNCTION_TESTS)
|
||||||
|
|||||||
@@ -21,9 +21,42 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
namespace AlibabaCloud {
|
namespace AlibabaCloud {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *data;
|
||||||
|
const char *pos;
|
||||||
|
const char *last;
|
||||||
|
} uploadContext;
|
||||||
|
|
||||||
|
static size_t readCallback(void *ptr, size_t size, size_t nmemb, void *stream) {
|
||||||
|
uploadContext *ctx = (uploadContext *)stream;
|
||||||
|
size_t len = 0;
|
||||||
|
|
||||||
|
if (ctx->pos >= ctx->last) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if ((size == 0) || (nmemb == 0) || (size * nmemb < 1)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
len = ctx->last - ctx->pos;
|
||||||
|
if (len > size * nmemb) {
|
||||||
|
len = size * nmemb;
|
||||||
|
}
|
||||||
|
memcpy(ptr, ctx->pos, len);
|
||||||
|
ctx->pos += len;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
size_t recvBody(char *ptr, size_t size, size_t nmemb, void *userdata) {
|
size_t recvBody(char *ptr, size_t size, size_t nmemb, void *userdata) {
|
||||||
std::ostringstream &out = *static_cast<std::ostringstream*>(userdata);
|
std::ostringstream &out = *static_cast<std::ostringstream*>(userdata);
|
||||||
out << std::string(ptr, nmemb*size);
|
out << std::string(ptr, nmemb*size);
|
||||||
@@ -99,9 +132,35 @@ CurlHttpClient::makeRequest(const HttpRequest &request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case HttpRequest::Method::Put:
|
|
||||||
|
case HttpRequest::Method::Put: {
|
||||||
|
uploadContext* ctx = (uploadContext *)malloc(sizeof(uploadContext));
|
||||||
|
// this is impossible, as the size is 24 Bytes
|
||||||
|
if (ctx == nullptr) {
|
||||||
|
return HttpResponseOutcome(
|
||||||
|
Error("MemoryAllocateError",
|
||||||
|
"There is not enough memory for http transfer."));
|
||||||
|
}
|
||||||
|
ctx->data = request.body();
|
||||||
|
ctx->pos = request.body();
|
||||||
|
ctx->last = ctx->pos + request.bodySize();
|
||||||
|
|
||||||
|
curl_easy_setopt(curlHandle_, CURLOPT_READFUNCTION, readCallback);
|
||||||
curl_easy_setopt(curlHandle_, CURLOPT_UPLOAD, 1L);
|
curl_easy_setopt(curlHandle_, CURLOPT_UPLOAD, 1L);
|
||||||
|
curl_easy_setopt(curlHandle_, CURLOPT_READDATA, ctx);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case HttpRequest::Method::Delete: {
|
||||||
|
curl_easy_setopt(curlHandle_, CURLOPT_CUSTOMREQUEST, "DELETE");
|
||||||
|
if (request.bodySize() > 0) {
|
||||||
|
curl_easy_setopt(curlHandle_, CURLOPT_POSTFIELDS, request.body());
|
||||||
|
} else {
|
||||||
|
curl_easy_setopt(curlHandle_, CURLOPT_POSTFIELDS, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,55 +27,76 @@ class mockCurlHttpClient : public CurlHttpClient {
|
|||||||
MOCK_METHOD1(makeRequest, HttpResponseOutcome (const HttpRequest &request));
|
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) {
|
TEST(CurlHttpClient, http_get) {
|
||||||
CurlHttpClient client;
|
CurlHttpClient client;
|
||||||
HttpRequest request;
|
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 url;
|
||||||
url.setHost("127.0.0.1");
|
url.setHost("127.0.0.1");
|
||||||
url.setPort(8021);
|
url.setPort(8021);
|
||||||
|
url.setPath("/anypath");
|
||||||
|
url.setQuery("k1=v1&k2=v2");
|
||||||
|
|
||||||
request.setMethod(HttpRequest::Method::Get);
|
request.setMethod(HttpRequest::Method::Get);
|
||||||
request.setUrl(url);
|
request.setUrl(url);
|
||||||
|
request.setHeader("Content-Type", "text/html");
|
||||||
request.setHeader("head1", "value1");
|
|
||||||
request.setHeader("head2", "value2");
|
|
||||||
HttpClient::HttpResponseOutcome out = client.makeRequest(request);
|
HttpClient::HttpResponseOutcome out = client.makeRequest(request);
|
||||||
EXPECT_TRUE(out.result().body() == testBody);
|
EXPECT_TRUE(std::string(out.result().body()) == "{\"k1\":\"v1\",\"k2\":\"v2\"}");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CurlHttpClient, http_post) {
|
||||||
|
CurlHttpClient client;
|
||||||
|
HttpRequest request;
|
||||||
|
std::string test_body = "any-body";
|
||||||
|
Url url;
|
||||||
|
url.setHost("127.0.0.1");
|
||||||
|
url.setPort(8021);
|
||||||
|
url.setPath("/anypath");
|
||||||
|
url.setQuery("k1=v1&k2=v2");
|
||||||
|
|
||||||
|
request.setMethod(HttpRequest::Method::Post);
|
||||||
|
request.setUrl(url);
|
||||||
|
request.setHeader("Content-Type", "text/html");
|
||||||
|
request.setBody(test_body.c_str(), test_body.size());
|
||||||
|
HttpClient::HttpResponseOutcome out = client.makeRequest(request);
|
||||||
|
EXPECT_TRUE(std::string(out.result().body()) == "POST: " + test_body);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CurlHttpClient, http_put) {
|
||||||
|
CurlHttpClient client;
|
||||||
|
HttpRequest request;
|
||||||
|
std::string test_body = "any-body";
|
||||||
|
Url url;
|
||||||
|
url.setHost("127.0.0.1");
|
||||||
|
url.setPort(8021);
|
||||||
|
url.setPath("/anypath");
|
||||||
|
url.setQuery("k1=v1&k2=v2");
|
||||||
|
|
||||||
request.setMethod(HttpRequest::Method::Put);
|
request.setMethod(HttpRequest::Method::Put);
|
||||||
request.setUrl(url);
|
request.setUrl(url);
|
||||||
|
request.setHeader("Content-Type", "text/html");
|
||||||
|
request.setBody(test_body.c_str(), test_body.size());
|
||||||
|
HttpClient::HttpResponseOutcome out = client.makeRequest(request);
|
||||||
|
EXPECT_TRUE(std::string(out.result().body()) == "PUT: " + test_body);
|
||||||
|
}
|
||||||
|
|
||||||
HttpClient::HttpResponseOutcome out1 = client.makeRequest(request);
|
TEST(CurlHttpClient, http_delete) {
|
||||||
EXPECT_TRUE(out1.result().body() == testBody);
|
CurlHttpClient client;
|
||||||
|
HttpRequest request;
|
||||||
|
std::string test_body = "any-body";
|
||||||
|
Url url;
|
||||||
|
url.setHost("127.0.0.1");
|
||||||
|
url.setPort(8021);
|
||||||
|
url.setPath("/anypath");
|
||||||
|
url.setQuery("k1=v1&k2=v2");
|
||||||
|
|
||||||
request.setMethod(HttpRequest::Method::Post);
|
request.setMethod(HttpRequest::Method::Delete);
|
||||||
request.setBody("test-body", 9);
|
|
||||||
request.setUrl(url);
|
request.setUrl(url);
|
||||||
|
request.setHeader("Content-Type", "text/html");
|
||||||
HttpClient::HttpResponseOutcome out2 = client.makeRequest(request);
|
request.setBody(test_body.c_str(), test_body.size());
|
||||||
EXPECT_TRUE(out2.result().body() == testBody);
|
HttpClient::HttpResponseOutcome out = client.makeRequest(request);
|
||||||
|
EXPECT_TRUE(std::string(out.result().body()) == "DELETE: " + test_body);
|
||||||
pclose(http);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CurlHttpClient, netWorkError) {
|
TEST(CurlHttpClient, netWorkError) {
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ namespace {
|
|||||||
"%26SignatureNonce%3DNwDAxvLU6tFE0DVb%26SignatureVersion%3D1.0"
|
"%26SignatureNonce%3DNwDAxvLU6tFE0DVb%26SignatureVersion%3D1.0"
|
||||||
"%26TimeStamp%3D2012-12-26T10%253A33%253A56Z%26Version%3D2013-01-10",
|
"%26TimeStamp%3D2012-12-26T10%253A33%253A56Z%26Version%3D2013-01-10",
|
||||||
"testsecret&");
|
"testsecret&");
|
||||||
cout << "aaaaaaaaaaa======================================= " << sign << endl;
|
|
||||||
EXPECT_TRUE("axE3FUHgDyfm9/+Iep0HpZXrRwE=" == sign);
|
EXPECT_TRUE("axE3FUHgDyfm9/+Iep0HpZXrRwE=" == sign);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ namespace {
|
|||||||
auto outcome = client.describeNatGateways(request);
|
auto outcome = client.describeNatGateways(request);
|
||||||
EXPECT_TRUE(outcome.isSuccess());
|
EXPECT_TRUE(outcome.isSuccess());
|
||||||
EXPECT_TRUE(outcome.error().errorCode().empty());
|
EXPECT_TRUE(outcome.error().errorCode().empty());
|
||||||
EXPECT_TRUE(outcome.result().getTotalCount() == 0);
|
EXPECT_TRUE(outcome.result().getTotalCount() >= 0);
|
||||||
ShutdownSdk();
|
ShutdownSdk();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
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)
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
#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);
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 */
|
|
||||||
|
|
||||||
@@ -1,164 +0,0 @@
|
|||||||
#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) {
|
|
||||||
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);
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
#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
|
|
||||||
39
test/httpserver/http_test_server.js
Normal file
39
test/httpserver/http_test_server.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
'use strict';
|
||||||
|
const express = require('express');
|
||||||
|
const bodyParser = require('body-parser');
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
app.listen(8021);
|
||||||
|
|
||||||
|
app.use(bodyParser.urlencoded({ extended: false }));
|
||||||
|
app.use(bodyParser.text({ type: 'text/html' }))
|
||||||
|
app.use(bodyParser.raw({ type: 'application/vnd.custom-type' }))
|
||||||
|
app.use(bodyParser.json({ type: 'application/*+json' }))
|
||||||
|
|
||||||
|
app.get('*', function (req, res) {
|
||||||
|
console.log('GET req.params: ', req.params);
|
||||||
|
console.log('GET req.query: ', req.query);
|
||||||
|
res.send(JSON.stringify(req.query));
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('*', function (req, res) {
|
||||||
|
console.log('POST req.params: ', req.params);
|
||||||
|
console.log('POST req.query: ', req.query);
|
||||||
|
console.log('POST req.body:', req.body);
|
||||||
|
res.send('POST: ' + req.body);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
app.put('*', function (req, res) {
|
||||||
|
console.log('PUT req.params: ', req.params);
|
||||||
|
console.log('PUT req.query: ', req.query);
|
||||||
|
console.log('PUT req.body: ', req.body);
|
||||||
|
res.send('PUT: ' + req.body);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.delete('*', function(req, res) {
|
||||||
|
console.log('DELETE req.params: ', req.params);
|
||||||
|
console.log('DELETE req.query: ', req.query);
|
||||||
|
console.log('DELETE req.body: ', req.body);
|
||||||
|
res.send("DELETE: " + req.body);
|
||||||
|
});
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"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}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
||||||
16
test/httpserver/package.json
Normal file
16
test/httpserver/package.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "http-server",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "simple http server for cpp sdk test",
|
||||||
|
"main": "index.js",
|
||||||
|
"dependencies": {
|
||||||
|
"express": "^4",
|
||||||
|
"body-parser": "^1.18"
|
||||||
|
},
|
||||||
|
"devDependencies": {},
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
||||||
27
unit_test.sh
27
unit_test.sh
@@ -3,6 +3,31 @@
|
|||||||
cd `dirname $0`
|
cd `dirname $0`
|
||||||
echo '-------build unit test----------'
|
echo '-------build unit test----------'
|
||||||
|
|
||||||
|
echo 'start a test http server'
|
||||||
|
NODE=`which nodejs`
|
||||||
|
|
||||||
|
if [ "$NODE" ]
|
||||||
|
then
|
||||||
|
echo ''
|
||||||
|
else
|
||||||
|
NODE=`which node`
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo 'node binary path: ' $NODE
|
||||||
|
|
||||||
|
server=`ps -ef | grep http_test_server | grep -v grep`
|
||||||
|
echo "check server: " $server
|
||||||
|
if [ "$server" ]
|
||||||
|
then
|
||||||
|
echo "server is on"
|
||||||
|
else
|
||||||
|
echo "server is off, start it"
|
||||||
|
cd test/httpserver
|
||||||
|
npm i
|
||||||
|
nohup $NODE http_test_server.js &
|
||||||
|
cd -
|
||||||
|
fi
|
||||||
|
|
||||||
MAKE=make
|
MAKE=make
|
||||||
if command -v python > /dev/null ; then
|
if command -v python > /dev/null ; then
|
||||||
MAKE="make -j $(python -c 'import multiprocessing as mp; print(int(mp.cpu_count()))')"
|
MAKE="make -j $(python -c 'import multiprocessing as mp; print(int(mp.cpu_count()))')"
|
||||||
@@ -15,7 +40,7 @@ rm -rf $UT_BUILD_DIR
|
|||||||
mkdir $UT_BUILD_DIR
|
mkdir $UT_BUILD_DIR
|
||||||
cd $UT_BUILD_DIR
|
cd $UT_BUILD_DIR
|
||||||
cmake -DBUILD_FUNCTION_TESTS=OFF -DBUILD_UNIT_TESTS=ON -DENABLE_COVERAGE=ON ..
|
cmake -DBUILD_FUNCTION_TESTS=OFF -DBUILD_UNIT_TESTS=ON -DENABLE_COVERAGE=ON ..
|
||||||
$MAKE core_ut httpserver_for_ut
|
$MAKE core_ut
|
||||||
|
|
||||||
echo '------- run unit test -----------'
|
echo '------- run unit test -----------'
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user