added readme
This commit is contained in:
parent
c49f947a9b
commit
5bfee7f955
3 changed files with 147 additions and 22 deletions
128
README.md
128
README.md
|
|
@ -1,3 +1,127 @@
|
||||||
# About
|
# Crafter.Network
|
||||||
|
|
||||||
C++ networking library
|
A cross-platform C++ networking library providing TCP and HTTP client/server functionality with modern C++ features.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Crafter.Network is a comprehensive networking library designed for modern C++ applications. It provides both TCP and HTTP networking capabilities with support for synchronous and asynchronous operations, making it suitable for a wide range of networking tasks.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **TCP Networking**: Client and server implementations for TCP connections
|
||||||
|
- **HTTP Support**: Full HTTP client and server implementations with routing capabilities
|
||||||
|
- **Asynchronous Operations**: Thread pool-based async operations for improved performance
|
||||||
|
- **Cross-Platform**: Built for Unix-like systems with socket-based networking
|
||||||
|
- **Modern C++**: Uses C++ modules, STL containers, and modern C++ features
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
The library follows a modular design using C++20 modules:
|
||||||
|
|
||||||
|
### Core Modules
|
||||||
|
- `Crafter.Network`: Main module that exports all components
|
||||||
|
- `Crafter.Network:ClientTCP`: TCP client implementation
|
||||||
|
- `Crafter.Network:ListenerTCP`: TCP server implementation
|
||||||
|
- `Crafter.Network:ClientHTTP`: HTTP client implementation
|
||||||
|
- `Crafter.Network:ListenerHTTP`: HTTP server implementation
|
||||||
|
- `Crafter.Network:HTTP`: HTTP protocol utilities and data structures
|
||||||
|
|
||||||
|
## Components
|
||||||
|
|
||||||
|
### TCP Components
|
||||||
|
|
||||||
|
#### ClientTCP
|
||||||
|
```cpp
|
||||||
|
// Create a TCP client
|
||||||
|
Crafter::ClientTCP client("localhost", 8080);
|
||||||
|
client.Send("Hello World", 11);
|
||||||
|
|
||||||
|
// Receive data
|
||||||
|
std::vector<char> data = client.RecieveSync();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### ListenerTCP
|
||||||
|
```cpp
|
||||||
|
// Create a TCP listener
|
||||||
|
auto callback = [](Crafter::ClientTCP* client) {
|
||||||
|
// Handle new connection
|
||||||
|
};
|
||||||
|
Crafter::ListenerTCP listener(8080, callback);
|
||||||
|
listener.ListenSyncSync(); // Synchronous listening
|
||||||
|
```
|
||||||
|
|
||||||
|
### HTTP Components
|
||||||
|
|
||||||
|
#### ClientHTTP
|
||||||
|
```cpp
|
||||||
|
// Create an HTTP client
|
||||||
|
Crafter::ClientHTTP client("httpbin.org", 80);
|
||||||
|
|
||||||
|
// Send HTTP request
|
||||||
|
std::string request = Crafter::CreateRequestHTTP("GET", "/get", "httpbin.org");
|
||||||
|
Crafter::HTTPResponse response = client.Send(request);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### ListenerHTTP
|
||||||
|
```cpp
|
||||||
|
// Create an HTTP listener with routes
|
||||||
|
std::unordered_map<std::string, std::function<std::string(const Crafter::HTTPRequest&)>> routes;
|
||||||
|
routes["/hello"] = [](const Crafter::HTTPRequest& req) {
|
||||||
|
return Crafter::CreateResponseHTTP("200 OK", "Hello World!");
|
||||||
|
};
|
||||||
|
|
||||||
|
Crafter::ListenerHTTP listener(8080, routes);
|
||||||
|
listener.Listen();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Build Configuration
|
||||||
|
|
||||||
|
The project uses a configuration system with multiple build targets:
|
||||||
|
|
||||||
|
- **base**: Core interfaces only
|
||||||
|
- **lib**: Static library build with dependencies
|
||||||
|
- **lib-debug**: Debug static library build
|
||||||
|
- **lib-shared**: Shared library build with dependencies
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
The library includes comprehensive tests covering:
|
||||||
|
- Compilation verification
|
||||||
|
- HTTP receive functionality
|
||||||
|
- HTTP send functionality
|
||||||
|
- HTTP send/receive operations
|
||||||
|
- Keep-alive HTTP operations
|
||||||
|
- Large HTTP data transfers
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Crafter.Thread: Thread pool management for asynchronous operations
|
||||||
|
|
||||||
|
## Usage Example
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <Crafter.Network>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
// Simple HTTP client example
|
||||||
|
Crafter::ClientHTTP client("httpbin.org", 80);
|
||||||
|
|
||||||
|
auto request = Crafter::CreateRequestHTTP("GET", "/get", "httpbin.org");
|
||||||
|
auto response = client.Send(request);
|
||||||
|
|
||||||
|
std::cout << "Status: " << response.status << std::endl;
|
||||||
|
std::cout << "Body: " << response.body << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This library is licensed under the GNU Lesser General Public License version 3.0. See [LICENSE](LICENSE) for more information.
|
||||||
|
|
||||||
|
## Copyright
|
||||||
|
|
||||||
|
Copyright (C) 2025 Catcrafts®
|
||||||
|
Catcrafts.net
|
||||||
|
|
@ -46,7 +46,9 @@ ClientHTTP::ClientHTTP(std::string host, std::uint16_t port): ClientHTTP(host.c_
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTPResponse ClientHTTP::Send(const char* request, std::uint32_t length) {
|
HTTPResponse ClientHTTP::Send(const char* request, std::uint32_t length) {
|
||||||
|
std::cout << "Send started" << std::endl;
|
||||||
client.Send(request, length);
|
client.Send(request, length);
|
||||||
|
std::cout << "Send Complete" << std::endl;
|
||||||
std::vector<char> buffer;
|
std::vector<char> buffer;
|
||||||
HTTPResponse response;
|
HTTPResponse response;
|
||||||
std::uint32_t i = 0;
|
std::uint32_t i = 0;
|
||||||
|
|
@ -54,11 +56,14 @@ HTTPResponse ClientHTTP::Send(const char* request, std::uint32_t length) {
|
||||||
while(true) {
|
while(true) {
|
||||||
try {
|
try {
|
||||||
buffer = client.RecieveSync();
|
buffer = client.RecieveSync();
|
||||||
|
std::cout << "Recieved: " << buffer.size() << std::endl;
|
||||||
} catch(const SocketClosedException& e) {
|
} catch(const SocketClosedException& e) {
|
||||||
|
std::cout << "Retry" << std::endl;
|
||||||
client.Stop();
|
client.Stop();
|
||||||
client.Connect();
|
client.Connect();
|
||||||
client.Send(request, length);
|
client.Send(request, length);
|
||||||
buffer = client.RecieveSync();
|
buffer = client.RecieveSync();
|
||||||
|
std::cout << "Recieved: " << buffer.size() << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(; i < buffer.size(); i++) {
|
for(; i < buffer.size(); i++) {
|
||||||
|
|
@ -104,27 +109,29 @@ HTTPResponse ClientHTTP::Send(const char* request, std::uint32_t length) {
|
||||||
i = 0;
|
i = 0;
|
||||||
}
|
}
|
||||||
headersComplete:;
|
headersComplete:;
|
||||||
|
std::cout << "Header complete" << std::endl;
|
||||||
i+=4;
|
i+=4;
|
||||||
std::unordered_map<std::string, std::string>::iterator it = response.headers.find("content-length");
|
std::unordered_map<std::string, std::string>::iterator it = response.headers.find("content-length");
|
||||||
if(it != response.headers.end())
|
if(it != response.headers.end())
|
||||||
{
|
{
|
||||||
const int lenght = std::stoi(it->second);
|
const int lenght = std::stoi(it->second);
|
||||||
|
std::cout << "Content lenght: " << lenght << std::endl;
|
||||||
response.body.resize(lenght, 0);
|
response.body.resize(lenght, 0);
|
||||||
if(i < buffer.size()){
|
if(i < buffer.size()){
|
||||||
std::memcpy(&response.body[0], buffer.data()+i, buffer.size()-i);
|
std::memcpy(&response.body[0], buffer.data()+i, buffer.size()-i);
|
||||||
}
|
}
|
||||||
const int remaining = lenght-(buffer.size()-i);
|
const int remaining = lenght-(buffer.size()-i);
|
||||||
|
std::cout << "Remain: " << remaining << std::endl;
|
||||||
if(remaining > 0){
|
if(remaining > 0){
|
||||||
int index = buffer.size()-i;
|
std::vector<char> bodyBuffer = client.RecieveUntilFullSync(remaining);
|
||||||
while(index < lenght) {
|
std::memcpy(&response.body[ buffer.size()-i], bodyBuffer.data(), bodyBuffer.size());
|
||||||
std::vector<char> bodyBuffer = client.RecieveSync(remaining);
|
std::cout << "Recieved: " << bodyBuffer.size() << std::endl;
|
||||||
std::memcpy(&response.body[index], bodyBuffer.data(), bodyBuffer.size());
|
|
||||||
index+=bodyBuffer.size();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
std::cout << "No Content Lenght" << std::endl;
|
||||||
std::unordered_map<std::string, std::string>::iterator it = response.headers.find("transfer-encoding");
|
std::unordered_map<std::string, std::string>::iterator it = response.headers.find("transfer-encoding");
|
||||||
if(it != response.headers.end() && it->second == "chunked") {
|
if(it != response.headers.end() && it->second == "chunked") {
|
||||||
|
std::cout << "Chunked" << std::endl;
|
||||||
while(i < buffer.size()){
|
while(i < buffer.size()){
|
||||||
std::string lenght;
|
std::string lenght;
|
||||||
int lenghtStart = i;
|
int lenghtStart = i;
|
||||||
|
|
@ -183,14 +190,17 @@ HTTPResponse ClientHTTP::Send(const char* request, std::uint32_t length) {
|
||||||
}
|
}
|
||||||
bodyFinished:;
|
bodyFinished:;
|
||||||
} else {
|
} else {
|
||||||
|
std::cout << "Recv until close" << std::endl;
|
||||||
std::vector<char> bodyBuffer = client.RecieveUntilCloseSync();
|
std::vector<char> bodyBuffer = client.RecieveUntilCloseSync();
|
||||||
response.body.resize((buffer.size()-i)+(bodyBuffer.size()), 0);
|
response.body.resize((buffer.size()-i)+(bodyBuffer.size()), 0);
|
||||||
if(i < buffer.size()){
|
if(i < buffer.size()){
|
||||||
std::memcpy(&response.body[0], buffer.data()+i, buffer.size()-i);
|
std::memcpy(&response.body[0], buffer.data()+i, buffer.size()-i);
|
||||||
}
|
}
|
||||||
std::memcpy(&response.body[buffer.size()-i], bodyBuffer.data(), bodyBuffer.size());
|
std::memcpy(&response.body[buffer.size()-i], bodyBuffer.data(), bodyBuffer.size());
|
||||||
|
std::cout << "Closed" << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
std::cout << "Response recieved" << std::endl;
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
HTTPResponse ClientHTTP::Send(std::string request) {
|
HTTPResponse ClientHTTP::Send(std::string request) {
|
||||||
|
|
|
||||||
|
|
@ -95,22 +95,13 @@ void ClientTCP::Stop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientTCP::Send(const void* buffer, std::uint32_t size) const {
|
void ClientTCP::Send(const void* buffer, std::uint32_t size) const {
|
||||||
const char* data = reinterpret_cast<const char*>(buffer);
|
int status = send(socketid, reinterpret_cast<const char*>(buffer), size, 0);
|
||||||
std::uint_fast32_t totalSent = 0;
|
|
||||||
const std::uint_fast32_t chunkSize = 1024;
|
|
||||||
|
|
||||||
while (totalSent < size) {
|
|
||||||
std::uint_fast32_t bytesToSend = std::min(chunkSize, size - totalSent);
|
|
||||||
int status = send(socketid, data + totalSent, bytesToSend, 0);
|
|
||||||
|
|
||||||
if (status == 0) {
|
if (status == 0) {
|
||||||
throw SocketClosedException();
|
throw SocketClosedException();
|
||||||
} else if (status < 0) {
|
} else if (status < 0) {
|
||||||
throw std::runtime_error(std::strerror(errno));
|
throw std::runtime_error(std::strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
totalSent += status;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
std::vector<char> ClientTCP::RecieveSync(std::uint32_t bufferSize) const {
|
std::vector<char> ClientTCP::RecieveSync(std::uint32_t bufferSize) const {
|
||||||
std::vector<char> totalBuffer(bufferSize);
|
std::vector<char> totalBuffer(bufferSize);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue