diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..1bde925 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,31 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug", + "type": "cppdbg", + "request": "launch", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "linux": { + "MIMode": "gdb", + "miDebuggerPath": "gdb", + "program": "${workspaceFolder}/output/main" + }, + "osx": { + "MIMode": "lldb", + "miDebuggerPath": "lldb-mi", + "program": "${workspaceFolder}/output/main" + }, + "windows": { + "MIMode": "gdb", + "miDebuggerPath": "gdb.exe", + "program": "${workspaceFolder}/output/main.exe" + }, + "preLaunchTask": "build" + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..a367b6d --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,84 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "type": "shell", + "group": { + "kind": "build", + "isDefault": true + }, + "windows": { + "command": "powershell", + "args": [ + "-c", + "mingw32-make" + ] + }, + "linux": { + "command": "bash", + "args": [ + "-c", + "make" + ] + }, + "osx": { + "command": "bash", + "args": [ + "-c", + "make" + ] + } + }, + { + "label": "build & run", + "type": "shell", + "windows": { + "command": "powershell", + "args": [ + "-c", + "'mingw32-make run'" + ] + }, + "linux": { + "command": "bash", + "args": [ + "-c", + "'make run'" + ] + }, + "osx": { + "command": "bash", + "args": [ + "-c", + "'make run'" + ] + } + }, + { + "label": "clean", + "type": "shell", + "windows": { + "command": "powershell", + "args": [ + "-c", + "'mingw32-make clean'" + ] + }, + "linux": { + "command": "bash", + "args": [ + "-c", + "'make clean'" + ] + }, + "osx": { + "command": "bash", + "args": [ + "-c", + "'make clean'" + ] + } + } + ] +} \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0811f9a --- /dev/null +++ b/Makefile @@ -0,0 +1,91 @@ +# +# 'make' build executable file 'main' +# 'make clean' removes all .o and executable files +# + +# define the Cpp compiler to use +CXX = g++ + +# define any compile-time flags +CXXFLAGS := -std=c++17 -Wall -Wextra -g + +# define library paths in addition to /usr/lib +# if I wanted to include libraries not in /usr/lib I'd specify +# their path using -Lpath, something like: +LFLAGS = + +# define output directory +OUTPUT := output + +# define source directory +SRC := src + +# define include directory +INCLUDE := include + +# define lib directory +LIB := lib + +ifeq ($(OS),Windows_NT) +MAIN := main.exe +SOURCEDIRS := $(SRC) +INCLUDEDIRS := $(INCLUDE) +LIBDIRS := $(LIB) +FIXPATH = $(subst /,\,$1) +RM := del /q /f +MD := mkdir +else +MAIN := main +SOURCEDIRS := $(shell find $(SRC) -type d) +INCLUDEDIRS := $(shell find $(INCLUDE) -type d) +LIBDIRS := $(shell find $(LIB) -type d) +FIXPATH = $1 +RM = rm -f +MD := mkdir -p +endif + +# define any directories containing header files other than /usr/include +INCLUDES := $(patsubst %,-I%, $(INCLUDEDIRS:%/=%)) + +# define the C libs +LIBS := $(patsubst %,-L%, $(LIBDIRS:%/=%)) + +# define the C source files +SOURCES := $(wildcard $(patsubst %,%/*.cpp, $(SOURCEDIRS))) + +# define the C object files +OBJECTS := $(SOURCES:.cpp=.o) + +# +# The following part of the makefile is generic; it can be used to +# build any executable just by changing the definitions above and by +# deleting dependencies appended to the file from 'make depend' +# + +OUTPUTMAIN := $(call FIXPATH,$(OUTPUT)/$(MAIN)) + +all: $(OUTPUT) $(MAIN) + @echo Executing 'all' complete! + +$(OUTPUT): + $(MD) $(OUTPUT) + +$(MAIN): $(OBJECTS) + $(CXX) $(CXXFLAGS) $(INCLUDES) -o $(OUTPUTMAIN) $(OBJECTS) $(LFLAGS) $(LIBS) + +# this is a suffix replacement rule for building .o's from .c's +# it uses automatic variables $<: the name of the prerequisite of +# the rule(a .c file) and $@: the name of the target of the rule (a .o file) +# (see the gnu make manual section about automatic variables) +.cpp.o: + $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@ + +.PHONY: clean +clean: + $(RM) $(OUTPUTMAIN) + $(RM) $(call FIXPATH,$(OBJECTS)) + @echo Cleanup complete! + +run: all + ./$(OUTPUTMAIN) + @echo Executing 'run: all' complete! \ No newline at end of file diff --git a/TCPServer.cpp b/TCPServer.cpp index 1164e46..a4c7668 100644 --- a/TCPServer.cpp +++ b/TCPServer.cpp @@ -6,35 +6,33 @@ namespace core { - TCPServer::TCPServer(EPoll &ePoll, IPAddress address, std::string text) - : TCPSocket(ePoll, text) { + TCPServer::TCPServer(EPoll &ePoll, IPAddress address, std::string text) + : TCPSocket(ePoll, text) { setDescriptor(socket(AF_INET, SOCK_STREAM, 0)); int yes = 1; setsockopt(getDescriptor(), SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); - if(bind(getDescriptor(), address.getPointer(), address.addressLength) < 0) + if(bind(getDescriptor(), address.getPointer(), address.addressLength) < 0) throw coreutils::Exception("Error on bind to socket: " + std::to_string(errno)); - if(listen(getDescriptor(), 20) < 0) + if(listen(getDescriptor(), 20) < 0) throw coreutils::Exception("Error on listen to socket"); } TCPServer::~TCPServer() { - coreutils::Log(coreutils::LOG_DEBUG_2) << "Closing server socket " << getDescriptor() << "."; + coreutils::Log(coreutils::LOG_DEBUG_2) << "Closing server socket " << getDescriptor() << "."; close(getDescriptor()); } - + void TCPServer::onDataReceived(std::string data) { - coreutils::Log(coreutils::LOG_DEBUG_2) << "entering TCPServer::onDataReceived socket " << getDescriptor() << "."; lock.lock(); TCPSession *session = accept(); - if(session) - sessions.push_back(session); + if(session) + sessions.push_back(session); lock.unlock(); - coreutils::Log(coreutils::LOG_DEBUG_2) << "Leaving TCPServer::onDataReceived socket " << getDescriptor() << "."; } - - TCPSession * TCPServer::accept() { - TCPSession *session = getSocketAccept(ePoll); - session->setDescriptor(::accept(getDescriptor(), (struct sockaddr *)&session->ipAddress.addr, &session->ipAddress.addressLength)); + + TCPSession * TCPServer::accept() { + TCPSession *session = getSocketAccept(ePoll); + session->setDescriptor(::accept(getDescriptor(), (struct sockaddr *)&session->ipAddress.addr, &session->ipAddress.addressLength)); // if(blackList && blackList->contains(session->ipAddress.getClientAddress())) { // session->shutdown(); // Log(LOG_WARN) << "Client at IP address " << session->ipAddress.getClientAddress() << " is blacklisted and was denied a connection."; @@ -48,36 +46,36 @@ namespace core { coreutils::Log(coreutils::LOG_DEBUG_2) << "Session started on socket " << session->getDescriptor() << "."; return session; } - - void TCPServer::removeFromSessionList(TCPSession *session) { + + void TCPServer::removeFromSessionList(TCPSession *session) { std::vector::iterator cursor; for(cursor = sessions.begin(); cursor < sessions.end(); ++cursor) if(*cursor == session) sessions.erase(cursor); } - - void TCPServer::sessionErrorHandler(std::string errorString, std::stringstream &out) { + + void TCPServer::sessionErrorHandler(std::string errorString, std::stringstream &out) { throw coreutils::Exception(errorString); - } - + } + TCPSession * TCPServer::getSocketAccept(EPoll &ePoll) { return new TCPSession(ePoll, *this); } - - void TCPServer::output(TCPSession *session) { + + void TCPServer::output(TCPSession *session) { std::stringstream out; - out << "|" << session->ipAddress.getClientAddressAndPort(); + out << "|" << session->ipAddress.getClientAddressAndPort(); session->send(); } - - int TCPServer::processCommand(std::string command, TCPSession *session, std::stringstream &data) { - int sequence = 0; + + int TCPServer::processCommand(std::string command, TCPSession *session, std::stringstream &data) { + int sequence = 0; for(auto *sessionx : sessions) { data << "|" << ++sequence; - sessionx->output(data); + sessionx->output(data); data << "|" << std::endl; } return 1; } - + } diff --git a/TCPServer.h b/TCPServer.h index f4f951e..0a7360d 100644 --- a/TCPServer.h +++ b/TCPServer.h @@ -8,23 +8,23 @@ #include "CommandList.h" namespace core { - + /// /// TCPServer /// /// Manage a socket connection as a TCP server type. Connections to the socket are processed through - /// the accept functionality. + /// the accept functionality. /// - /// A list of connections is maintained in a vector object. + /// A list of connections is maintained in a vector object. /// /// This object extends the BMACommand object as well so it can be added to a Console object and /// process commands to display status information. /// - + class TCPServer : public TCPSocket, public Command { - + public: - + /// /// The constructor for the BMATCPSocket object. /// @@ -34,90 +34,90 @@ namespace core { /// @param commandName the name of the command used to invoke the status display for this object. /// @return the instance of the BMATCPServerSocket. /// - + TCPServer(EPoll &ePoll, IPAddress address, std::string text = ""); - + /// /// The destructor for this object. /// - + ~TCPServer(); - - /// - /// If not NULL the blacklist object can be assigned to this server socket and the server - /// IP addresses connecting to the server attempting to accept a socket are contained in - /// this list then the connection is rejected and no accept is granted. - /// - - IPAddressList *blackList; /// /// If not NULL the blacklist object can be assigned to this server socket and the server - /// IP addresses connecting to the server attempting to accept a socket are contained in - /// this list then the connection is rejected and no accept is granted. + /// IP addresses connecting to the server attempting to accept a socket are contained in + /// this list then the connection is rejected and no accept is granted. /// - + + IPAddressList *blackList; + + /// + /// If not NULL the blacklist object can be assigned to this server socket and the server + /// IP addresses connecting to the server attempting to accept a socket are contained in + /// this list then the connection is rejected and no accept is granted. + /// + IPAddressList *whiteList; void removeFromSessionList(TCPSession *session); - virtual void sessionErrorHandler(std::string errorString, std::stringstream &out); - + virtual void sessionErrorHandler(std::string errorString, std::stringstream &out); + /// /// getSocketAccept is designed to allow a polymorphic extension of this object to /// return a type of object that extends the definition of the server socket. /// Returning the appropriate session object that extends from Session provides - /// the mechanism where the server can select the protocol dialog for the desired + /// the mechanism where the server can select the protocol dialog for the desired /// service. - /// - + /// + virtual TCPSession * getSocketAccept(EPoll &epoll); - + void output(TCPSession *session); /// sessions; - + /// - /// The commands object is a CommandList and is used to store Command objects to be + /// The commands object is a CommandList and is used to store Command objects to be /// parsed and run as data comes into the session. /// - + CommandList commands; - + protected: - + /// /// Override the virtual dataReceived since for the server these /// are requests to accept the new connection socket. - /// No data is to be read or written when this method is called. It is the response to - /// the fact that a new connection is coming into the system + /// No data is to be read or written when this method is called. It is the response to + /// the fact that a new connection is coming into the system /// /// @param data the pointer to the buffer containing the received data. /// @param length the length of the associated data buffer. /// - - void onDataReceived(std::string data) override; - + + void onDataReceived(std::string data) override; + /// /// This method is called when the Command associated with this object is requested - /// because a user has typed in the associated command name on a command entry line. + /// because a user has typed in the associated command name on a command entry line. /// /// @param the session object to write the output to. /// - + int processCommand(std::string command, TCPSession *session, std::stringstream &data) override; - + private: TCPSession * accept(); std::mutex lock; - + }; - + } #endif diff --git a/TCPSession.cpp b/TCPSession.cpp index 5ea0060..fa6adf6 100644 --- a/TCPSession.cpp +++ b/TCPSession.cpp @@ -33,46 +33,41 @@ namespace core { void TCPSession::onDataReceived(char *data, int len) { if(len > 0) { - lineBuffer = (char *)realloc(lineBuffer, lineBufferSize + len); - memcpy(lineBuffer + lineBufferSize, data, len); - lineBufferSize += len; - while(lineBufferSize > 0) { - switch(mode) { - case LINE: - lineLength = strcspn(lineBuffer, "\r\n"); - if(lineLength == lineBufferSize) - break; - onLineReceived(std::string(lineBuffer, lineLength)); - if(lineBuffer[lineLength] == '\r') - ++lineLength; - if(lineBuffer[lineLength] == '\n') - ++lineLength; - lineBufferSize -= lineLength; - if(lineBufferSize > 0) - memmove(lineBuffer, lineBuffer + lineLength, lineBufferSize); - lineBuffer = (char *)realloc(lineBuffer, lineBufferSize); - break; - case BLOCK: - if(lineBufferSize >= blockLength) { - onBlockReceived(std::string(lineBuffer, blockLength)); - lineBufferSize -= blockLength; - if(lineBufferSize > 0) - memmove(lineBuffer, lineBuffer + blockLength, lineBufferSize); - lineBuffer = (char *)realloc(lineBuffer, lineBufferSize); - } - break; - } - } + lineBuffer = (char *)realloc(lineBuffer, lineBufferSize + len); + memcpy(lineBuffer + lineBufferSize, data, len); + lineBufferSize += len; + while(lineBufferSize > 0) { + if(blockSize == 0) { + lineLength = strcspn(lineBuffer, "\r\n"); + if(lineLength == lineBufferSize) + break; + onLineReceived(std::string(lineBuffer, lineLength)); + if(lineBuffer[lineLength] == '\r') + ++lineLength; + if(lineBuffer[lineLength] == '\n') + ++lineLength; + lineBufferSize -= lineLength; + if(lineBufferSize > 0) + memmove(lineBuffer, lineBuffer + lineLength, lineBufferSize); + lineBuffer = (char *)realloc(lineBuffer, lineBufferSize); + } else { + if(lineBufferSize >= blockLength) { + onBlockReceived(std::string(lineBuffer, blockLength)); + lineBufferSize -= blockLength; + if(lineBufferSize > 0) + memmove(lineBuffer, lineBuffer + blockLength, lineBufferSize); + lineBuffer = (char *)realloc(lineBuffer, lineBufferSize); + } + } + } } } - void TCPSession::setMode(core::Mode mode, int blockSize) { - this->mode = mode; + void TCPSession::setBlockSize(int blockSize) { this->blockSize = blockSize; } void TCPSession::onLineReceived(std::string line) { - coreutils::Log(coreutils::LOG_DEBUG_3) << "[" << line << "]"; protocol(line); send(); if(term) diff --git a/TCPSession.h b/TCPSession.h index 6fb4e98..53fd52c 100644 --- a/TCPSession.h +++ b/TCPSession.h @@ -7,7 +7,6 @@ namespace core { class Command; - enum Mode {LINE, BLOCK, PACKET}; class TCPServer; /// @@ -137,13 +136,12 @@ namespace core { virtual void protocol(std::string data); /// - /// Use the setMode method to set the receiving mode for the data on this socket. - /// Data can be received in LINE mode, which will receive data from the socket one - /// line at a time, or BLOCK mode where a certain specified data block is received - /// before calling the onBlockReceived method. + /// Use setBlockSize to set the amount of data that should be read at once from the + /// session data buffer. + /// If this value is set to 0 then the data will be retrieved /// - void setMode(core::Mode mode, int size = 0); + void setBlockSize(int size = 0); private: char *lineBuffer = NULL; @@ -152,8 +150,7 @@ namespace core { int blockLength = 0; std::mutex mtx; bool term = false; - core::Mode mode = LINE; - int blockSize; + int blockSize = 0; }; diff --git a/TCPSocket.cpp b/TCPSocket.cpp index 3ac3606..efd4754 100644 --- a/TCPSocket.cpp +++ b/TCPSocket.cpp @@ -4,23 +4,23 @@ #include "Exception.h" namespace core { - + TCPSocket::TCPSocket(EPoll &ePoll) : Socket(ePoll) {} TCPSocket::TCPSocket(EPoll &ePoll, std::string text) : Socket(ePoll, text) {} - + TCPSocket::~TCPSocket() {} - + void TCPSocket::connect(IPAddress &address) { setDescriptor(socket(AF_INET, SOCK_STREAM, 0)); - if(::connect(getDescriptor(), (struct sockaddr *)&address.addr, address.addressLength) == -1) - throw coreutils::Exception("Error on connect to TCP socket."); - + if(::connect(getDescriptor(), (struct sockaddr *)&address.addr, address.addressLength) == -1) + throw coreutils::Exception("Error on connect to TCP socket."); + } - + void TCPSocket::output(std::stringstream &out) { - out << "|" << ipAddress.getClientAddressAndPort(); + out << "|" << ipAddress.getClientAddressAndPort(); } - + } diff --git a/TCPSocket.h b/TCPSocket.h index a5edfc7..fd888db 100644 --- a/TCPSocket.h +++ b/TCPSocket.h @@ -9,12 +9,12 @@ namespace core { /// /// TCPSocket - /// + /// /// Provides a network TCP socket. - /// + /// /// For accessing TCP network functions use this object. The connection oriented nature of TCP - /// provides a single client persistent connection with data error correction and a durable - /// synchronous data connection. + /// provides a single client persistent connection with data error correction and a durable + /// synchronous data connection. /// class TCPSocket : public Socket { @@ -24,20 +24,20 @@ namespace core { TCPSocket(EPoll &ePoll); TCPSocket(EPoll &ePoll, std::string text); ~TCPSocket(); - + void connect(IPAddress &address); - + IPAddress ipAddress; - + /// - /// The output method is called by a socket session (BMASession) and - /// will output the detail information for the client socket. When extending - /// BMATCPSocket or BMASession you can override the method to add attributes - /// to the list. + /// The output method is called by a socket session (TCPSession) and + /// will output the detail information for the client socket. When extending + /// BMATCPSocket or BMASession you can override the method to add attributes + /// to the list. /// virtual void output(std::stringstream &out); - + }; } diff --git a/output/main b/output/main new file mode 100755 index 0000000..99f2acb Binary files /dev/null and b/output/main differ diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..d8e796c --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,6 @@ +#include + +int main(int argc, char *argv[]) +{ + std::cout << "Hello world!" << std::endl; +} \ No newline at end of file diff --git a/src/workspace.code-workspace b/src/workspace.code-workspace new file mode 100644 index 0000000..0221949 --- /dev/null +++ b/src/workspace.code-workspace @@ -0,0 +1,38 @@ +{ + "folders": [ + { + "path": "../../CoreUtils" + }, + { + "path": ".." + }, + { + "path": "../../HTTPServer" + } + ], + "settings": {}, + "launch": { + "version": "0.2.0", + "configurations": [ + { + "name": "(gdb) Launch", + "type": "cppdbg", + "request": "launch", + "program": "/home/barant/Development/HTTPServer/HTTPServer", + "args": [], + "stopAtEntry": false, + "cwd": "/home/barant/Development/HTTPServer/", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } + ] + } +} \ No newline at end of file