use github repo as central

This commit is contained in:
Jackson Tian
2019-01-09 14:07:33 +08:00
parent 9664fb1b00
commit bc0df2dcec
46 changed files with 1864 additions and 29 deletions

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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 */

View File

@@ -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;
}
}
}
}

View File

@@ -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

View File

@@ -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
test/httpserver/main.cpp Normal file
View File

@@ -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;
}