Removed socket mutexes. Added more tests. Improved examples.
Socket mutexes are no longer really required, and so have been removed. Added more tests for network encoding functions, and the URL parser. The URL parser now returns a path preceeded with a '/' instead of cutting it out. Added get_uri() to URL, for getting the whole URI, so users don't have to concat it themselves from the more specialised functions. Fixed default socket connect timeout checking for the wrong value. Fixed request_type_strings not containing all of the possible request types. Fixed README using old socket close syntax. Cleaned up the examples a bit.
This commit is contained in:
parent
ad847f0052
commit
ff25d11089
@ -131,7 +131,7 @@ while(true)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Close connection
|
//Close connection
|
||||||
client.close();
|
client.close_socket();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
After binding to the port, we infinitely try and receive a new request, construct a response with the body of 'Hello, World!' and send it back to the client before closing the socket. fr::HttpRequest, and fr::HttpResponse both inherit fr::Sendable, which allows them to be sent and received through sockets just like fr::Packets.
|
After binding to the port, we infinitely try and receive a new request, construct a response with the body of 'Hello, World!' and send it back to the client before closing the socket. fr::HttpRequest, and fr::HttpResponse both inherit fr::Sendable, which allows them to be sent and received through sockets just like fr::Packets.
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
add_executable(simple_client tcpsocket_client.cpp)
|
add_executable(simple_http_client SimpleHttpClient.cpp)
|
||||||
target_link_libraries(simple_client frnetlib)
|
target_link_libraries(simple_http_client frnetlib)
|
||||||
|
|
||||||
add_executable(simple_server tcpsocket_server.cpp)
|
add_executable(simple_http_server SimpleHttpServer.cpp)
|
||||||
target_link_libraries(simple_server frnetlib)
|
target_link_libraries(simple_http_server frnetlib)
|
||||||
|
|
||||||
install(TARGETS simple_client simple_server
|
install(TARGETS simple_http_client simple_http_server
|
||||||
DESTINATION "bin")
|
DESTINATION "bin")
|
||||||
|
|||||||
54
examples/simple_server_and_client/SimpleHttpClient.cpp
Normal file
54
examples/simple_server_and_client/SimpleHttpClient.cpp
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
//
|
||||||
|
// Created by fred.nicolson on 01/02/18.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <frnetlib/TcpSocket.h>
|
||||||
|
#include <frnetlib/HttpRequest.h>
|
||||||
|
#include <frnetlib/URL.h>
|
||||||
|
#include <frnetlib/HttpResponse.h>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
//Get an address to query from stdin
|
||||||
|
std::string url;
|
||||||
|
std::cout << "Enter URL: ";
|
||||||
|
std::cin >> url;
|
||||||
|
|
||||||
|
//Parse it into something easy to use
|
||||||
|
fr::URL parsed_url(url);
|
||||||
|
if(parsed_url.get_port().empty())
|
||||||
|
{
|
||||||
|
std::cerr << "No schema or port specified. Unable to connect." << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Try to connect to the parsed address
|
||||||
|
fr::TcpSocket socket;
|
||||||
|
if(socket.connect(parsed_url.get_host(), parsed_url.get_port(), {}) != fr::Socket::Success)
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to connect to the specified URL" << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Construct a request, requesting the user provided URI
|
||||||
|
fr::HttpRequest request;
|
||||||
|
request.set_uri(parsed_url.get_uri());
|
||||||
|
if(socket.send(request) != fr::Socket::Success)
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to send HTTP request" << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Now wait for a response
|
||||||
|
fr::HttpResponse response;
|
||||||
|
if(socket.receive(response) != fr::Socket::Success)
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to receive HTTP response" << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Print out the response body
|
||||||
|
std::cout << response.get_body() << std::endl;
|
||||||
|
|
||||||
|
}
|
||||||
52
examples/simple_server_and_client/SimpleHttpServer.cpp
Normal file
52
examples/simple_server_and_client/SimpleHttpServer.cpp
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
//
|
||||||
|
// Created by fred.nicolson on 01/02/18.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <frnetlib/HttpRequest.h>
|
||||||
|
#include <frnetlib/HttpResponse.h>
|
||||||
|
#include <frnetlib/TcpListener.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
fr::Socket::Status err;
|
||||||
|
fr::TcpSocket client;
|
||||||
|
fr::TcpListener listener;
|
||||||
|
|
||||||
|
//Bind to a port
|
||||||
|
if((err = listener.listen("8081")) != fr::Socket::Success)
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to bind to port: " << err << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
//Accept a new connection
|
||||||
|
if((err = listener.accept(client)) != fr::Socket::Success)
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to accept new connection: " << err << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Receive client HTTP request
|
||||||
|
fr::HttpRequest request;
|
||||||
|
if((err = client.receive(request)) != fr::Socket::Success)
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to receive request from client: " << err << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Construct a response
|
||||||
|
fr::HttpResponse response;
|
||||||
|
response.set_body("<h1>Hello, World!</h1>");
|
||||||
|
|
||||||
|
//Send it
|
||||||
|
if((err = client.send(response)) != fr::Socket::Success)
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to send response to client: " << err << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Close connection
|
||||||
|
client.close_socket();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,90 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <frnetlib/Packet.h>
|
|
||||||
#include <frnetlib/TcpSocket.h>
|
|
||||||
#include <frnetlib/TcpListener.h>
|
|
||||||
|
|
||||||
#define SERVER_IP "127.0.0.1"
|
|
||||||
#define SERVER_PORT "8081"
|
|
||||||
|
|
||||||
|
|
||||||
int send_a_packet(fr::TcpSocket &socket)
|
|
||||||
{
|
|
||||||
std::cout << "Going to send something..." << std::endl;
|
|
||||||
|
|
||||||
//Send the request
|
|
||||||
fr::Packet packet;
|
|
||||||
packet << "Hello there, I am " << (float)1.2 << " years old";
|
|
||||||
if(socket.send(packet) != fr::Socket::Success)
|
|
||||||
{
|
|
||||||
//Failed to send packet
|
|
||||||
std::cout << "Seems got something wrong when sending" << std::endl;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Receive a response
|
|
||||||
std::cout << "Waiting for a response..." << std::endl;
|
|
||||||
if(socket.receive(packet) != fr::Socket::Success)
|
|
||||||
{
|
|
||||||
std::cout << "Failed to receive server response!" << std::endl;
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Extract the response, you can surround this in a try/catch block to catch errors
|
|
||||||
std::string str1, str2;
|
|
||||||
float age;
|
|
||||||
packet >> str1 >> age >> str2;
|
|
||||||
std::cout << "Server sent: " << str1 << ", " << age << ", " << str2 << "\n\n\n" << std::endl;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
|
|
||||||
//Try and connect to the server, create a new TCP object for the job and then connect
|
|
||||||
fr::TcpSocket socket; //Use an fr::SSLSocket if SSL
|
|
||||||
if(socket.connect(SERVER_IP, SERVER_PORT, std::chrono::seconds(20)) != fr::Socket::Success)
|
|
||||||
{
|
|
||||||
//Failed to connect
|
|
||||||
std::cout << "Failed to connect to: " << SERVER_IP << ":" << SERVER_PORT << std::endl;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//For storing user input and send_a_packet response
|
|
||||||
std::string op_str;
|
|
||||||
int rtn = 0;
|
|
||||||
|
|
||||||
//Keep going until either we, or the server closes the connection
|
|
||||||
while(true)
|
|
||||||
{
|
|
||||||
//Ask the user what to do
|
|
||||||
std::cout << "Choose what you want to do, `c` for `continue`, `q` for `quit`:" << std::endl;
|
|
||||||
std::cin >> op_str;
|
|
||||||
if(op_str.length() > 1)
|
|
||||||
{
|
|
||||||
std::cout << "Seems that you inputted more than one character, please retry." << std::endl;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(op_str[0])
|
|
||||||
{
|
|
||||||
case 'c':
|
|
||||||
std::cout << "continue" << std::endl;
|
|
||||||
rtn = send_a_packet(socket);
|
|
||||||
break;
|
|
||||||
case 'q':
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
std::cout << "Invalid input!" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Exit/error check
|
|
||||||
if(op_str[0] == 'q')
|
|
||||||
break;
|
|
||||||
if(rtn != 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "All done, bye!" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,78 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <frnetlib/Packet.h>
|
|
||||||
#include <frnetlib/TcpSocket.h>
|
|
||||||
#include <frnetlib/TcpListener.h>
|
|
||||||
#define PORT "8081"
|
|
||||||
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
//Create a new TCP socket to maintain connections, and a new Tcp Listener to accept connections
|
|
||||||
fr::TcpSocket client; //fr::SSLSocket for SSL
|
|
||||||
fr::TcpListener listener; //fr::SSLListener for HTTPS
|
|
||||||
|
|
||||||
//Bind to a port
|
|
||||||
if(listener.listen(PORT) != fr::Socket::Success)
|
|
||||||
{
|
|
||||||
std::cout << "Failed to bind to port: " << PORT << std::endl;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "Listener is listening on port: " << PORT << "..." << std::endl;
|
|
||||||
|
|
||||||
//Start accepting connections
|
|
||||||
while(true)
|
|
||||||
{
|
|
||||||
std::cout << "Waiting for a new connection ..." << std::endl;
|
|
||||||
|
|
||||||
//Accept a new connection
|
|
||||||
if(listener.accept(client) != fr::Socket::Success)
|
|
||||||
{
|
|
||||||
std::cout << "Failed to accept client, shutdown" << std::endl;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
while(true) //Infinite loop to keep the connection active
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
//Receive an fr::Packet from the client
|
|
||||||
fr::Packet packet;
|
|
||||||
if(client.receive(packet) != fr::Socket::Success)
|
|
||||||
{
|
|
||||||
std::cout << "Failed to receive request" << std::endl;
|
|
||||||
client.close_socket();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Extract the data from the packet
|
|
||||||
std::string str1, str2;
|
|
||||||
float age;
|
|
||||||
packet >> str1 >> age >> str2;
|
|
||||||
|
|
||||||
//Print out what we got
|
|
||||||
std::cout << "Client sent:" << str1 << ", " << age << ", " << str2 << std::endl;
|
|
||||||
|
|
||||||
//Send back the same packet
|
|
||||||
if(client.send(packet) != fr::Socket::Success)
|
|
||||||
{
|
|
||||||
throw std::string("Failed to send packet to client");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(const std::exception &e)
|
|
||||||
{
|
|
||||||
//Print out what happened
|
|
||||||
std::cout << "Error: " << e.what() << std::endl;
|
|
||||||
|
|
||||||
//Close connection
|
|
||||||
client.close_socket();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ namespace fr
|
|||||||
Put = 3,
|
Put = 3,
|
||||||
Delete = 4,
|
Delete = 4,
|
||||||
Patch = 5,
|
Patch = 5,
|
||||||
RequestTypeCount = 3,
|
RequestTypeCount = 6, //Keep me at the end and updated
|
||||||
};
|
};
|
||||||
enum RequestStatus
|
enum RequestStatus
|
||||||
{
|
{
|
||||||
|
|||||||
@ -47,20 +47,10 @@ namespace fr
|
|||||||
virtual ~Socket() noexcept = default;
|
virtual ~Socket() noexcept = default;
|
||||||
Socket(Socket &&o) noexcept
|
Socket(Socket &&o) noexcept
|
||||||
{
|
{
|
||||||
outbound_mutex.lock();
|
|
||||||
inbound_mutex.lock();
|
|
||||||
o.inbound_mutex.lock();
|
|
||||||
o.outbound_mutex.lock();
|
|
||||||
|
|
||||||
remote_address = std::move(o.remote_address);
|
remote_address = std::move(o.remote_address);
|
||||||
is_blocking = o.is_blocking;
|
is_blocking = o.is_blocking;
|
||||||
ai_family = o.ai_family;
|
ai_family = o.ai_family;
|
||||||
max_receive_size = o.max_receive_size;
|
max_receive_size = o.max_receive_size;
|
||||||
|
|
||||||
outbound_mutex.unlock();
|
|
||||||
inbound_mutex.unlock();
|
|
||||||
o.inbound_mutex.unlock();
|
|
||||||
o.outbound_mutex.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -195,7 +185,7 @@ namespace fr
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Sets the maximum receivable size that may be received by the socket. This does
|
* Sets the maximum receivable size that may be received by the socket. This does
|
||||||
* not apply to receive_raw(), but only things like fr::Packet, or HTTP responses.
|
* not apply to receive_raw(), but only things like fr::Packet.
|
||||||
*
|
*
|
||||||
* If a client attempts to send a packet larger than sz bytes, then
|
* If a client attempts to send a packet larger than sz bytes, then
|
||||||
* the client will be disconnected and an fr::Socket::MaxPacketSizeExceeded
|
* the client will be disconnected and an fr::Socket::MaxPacketSizeExceeded
|
||||||
@ -214,6 +204,7 @@ namespace fr
|
|||||||
* Gets the max packet size. See set_max_packet_size
|
* Gets the max packet size. See set_max_packet_size
|
||||||
* for more information.
|
* for more information.
|
||||||
*
|
*
|
||||||
|
*
|
||||||
* @return The max packet size
|
* @return The max packet size
|
||||||
*/
|
*/
|
||||||
inline uint32_t get_max_receive_size()
|
inline uint32_t get_max_receive_size()
|
||||||
@ -240,8 +231,6 @@ namespace fr
|
|||||||
|
|
||||||
std::string remote_address;
|
std::string remote_address;
|
||||||
bool is_blocking;
|
bool is_blocking;
|
||||||
std::mutex outbound_mutex;
|
|
||||||
std::mutex inbound_mutex;
|
|
||||||
int ai_family;
|
int ai_family;
|
||||||
uint32_t max_receive_size;
|
uint32_t max_receive_size;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -32,7 +32,10 @@ namespace fr
|
|||||||
explicit URL(const std::string &url);
|
explicit URL(const std::string &url);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Parses a given URL, extracting its various components
|
* Parses a given URL, extracting its various components. This will always
|
||||||
|
* try to make something out of the provided URL. It will also try its best
|
||||||
|
* to guess any potentially missing information. For example, if the URL is
|
||||||
|
* https://example.com, then the 'port' will be guessed as 443.
|
||||||
*
|
*
|
||||||
* @param url The URL to parse
|
* @param url The URL to parse
|
||||||
*/
|
*/
|
||||||
@ -86,6 +89,25 @@ namespace fr
|
|||||||
return fragment;
|
return fragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Returns the combination of other URL elements into a single URI string.
|
||||||
|
*
|
||||||
|
* The URL is everything after the IP/Address & Port.
|
||||||
|
*
|
||||||
|
* @return The URL's URI
|
||||||
|
*/
|
||||||
|
inline std::string get_uri() const
|
||||||
|
{
|
||||||
|
std::string result;
|
||||||
|
if(!get_path().empty())
|
||||||
|
result += get_path();
|
||||||
|
if(!get_query().empty())
|
||||||
|
result += "?" + get_query();
|
||||||
|
if(!get_fragment().empty())
|
||||||
|
result += "#" + get_fragment();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Converts a string to a scheme enum.
|
* Converts a string to a scheme enum.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -9,14 +9,19 @@
|
|||||||
|
|
||||||
namespace fr
|
namespace fr
|
||||||
{
|
{
|
||||||
const static std::string request_type_strings[Http::RequestType::RequestTypeCount] = {"UNKNOWN", "GET", "POST"};
|
const static std::string request_type_strings[Http::RequestType::RequestTypeCount] = {"UNKNOWN",
|
||||||
|
"GET",
|
||||||
|
"POST",
|
||||||
|
"PUT",
|
||||||
|
"DELETE",
|
||||||
|
"PATCH"};
|
||||||
|
|
||||||
Http::Http()
|
Http::Http()
|
||||||
: request_type(Unknown),
|
: request_type(Unknown),
|
||||||
uri("/"),
|
uri("/"),
|
||||||
status(Ok)
|
status(Ok)
|
||||||
{
|
{
|
||||||
|
static_assert(Http::RequestType::RequestTypeCount == 6, "Please update request_type_strings");
|
||||||
}
|
}
|
||||||
|
|
||||||
Http::RequestType Http::get_type() const
|
Http::RequestType Http::get_type() const
|
||||||
|
|||||||
@ -44,11 +44,9 @@ namespace fr
|
|||||||
|
|
||||||
//Ensure that body doesn't exceed maximum length
|
//Ensure that body doesn't exceed maximum length
|
||||||
if(body.size() > MAX_HTTP_BODY_SIZE)
|
if(body.size() > MAX_HTTP_BODY_SIZE)
|
||||||
{
|
|
||||||
return fr::Socket::HttpBodyTooBig;
|
return fr::Socket::HttpBodyTooBig;
|
||||||
}
|
|
||||||
|
|
||||||
//Cut off any data if it exceeds content length
|
//Cut off any data if it exceeds content length, todo: potentially an issue, could cut the next request off
|
||||||
if(body.size() > content_length)
|
if(body.size() > content_length)
|
||||||
body.resize(content_length);
|
body.resize(content_length);
|
||||||
else if(body.size() < content_length)
|
else if(body.size() < content_length)
|
||||||
|
|||||||
@ -41,7 +41,6 @@ namespace fr
|
|||||||
if(!connected())
|
if(!connected())
|
||||||
return Socket::Disconnected;
|
return Socket::Disconnected;
|
||||||
|
|
||||||
std::lock_guard<std::mutex> guard(inbound_mutex);
|
|
||||||
return obj.receive(this);
|
return obj.receive(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -23,7 +23,6 @@ namespace fr
|
|||||||
|
|
||||||
Socket::Status TcpSocket::send_raw(const char *data, size_t size)
|
Socket::Status TcpSocket::send_raw(const char *data, size_t size)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> guard(outbound_mutex);
|
|
||||||
size_t sent = 0;
|
size_t sent = 0;
|
||||||
while(sent < size)
|
while(sent < size)
|
||||||
{
|
{
|
||||||
@ -128,7 +127,7 @@ namespace fr
|
|||||||
|
|
||||||
//Wait for the socket to do something/expire
|
//Wait for the socket to do something/expire
|
||||||
timeval tv = {};
|
timeval tv = {};
|
||||||
tv.tv_sec = timeout.count() == -1 ? DEFAULT_SOCKET_TIMEOUT : timeout.count();
|
tv.tv_sec = timeout.count() == 0 ? DEFAULT_SOCKET_TIMEOUT : timeout.count();
|
||||||
tv.tv_usec = 0;
|
tv.tv_usec = 0;
|
||||||
fd_set set = {};
|
fd_set set = {};
|
||||||
FD_ZERO(&set);
|
FD_ZERO(&set);
|
||||||
|
|||||||
10
src/URL.cpp
10
src/URL.cpp
@ -38,12 +38,12 @@ namespace fr
|
|||||||
{
|
{
|
||||||
auto scheme_pos = scheme_string_map.find(to_lower(url.substr(0, pos)));
|
auto scheme_pos = scheme_string_map.find(to_lower(url.substr(0, pos)));
|
||||||
scheme = (scheme_pos == scheme_string_map.end()) ? URL::Unknown : scheme_pos->second;
|
scheme = (scheme_pos == scheme_string_map.end()) ? URL::Unknown : scheme_pos->second;
|
||||||
parse_offset = pos + 3;
|
parse_offset = pos + 3; // skip the ://
|
||||||
}
|
}
|
||||||
|
|
||||||
//Check to see if there's a port
|
//Check to see if there's a port
|
||||||
pos = url.find(':', parse_offset);
|
pos = url.find(':', parse_offset);
|
||||||
if(pos != std::string::npos)
|
if(pos != std::string::npos) //A port is provided
|
||||||
{
|
{
|
||||||
//Store host
|
//Store host
|
||||||
host = url.substr(parse_offset, pos - parse_offset);
|
host = url.substr(parse_offset, pos - parse_offset);
|
||||||
@ -55,7 +55,7 @@ namespace fr
|
|||||||
port = url.substr(pos + 1, port_end - pos - 1);
|
port = url.substr(pos + 1, port_end - pos - 1);
|
||||||
parse_offset = port_end + 1;
|
parse_offset = port_end + 1;
|
||||||
}
|
}
|
||||||
else
|
else //There's no port
|
||||||
{
|
{
|
||||||
//Store host
|
//Store host
|
||||||
pos = url.find('/', parse_offset);
|
pos = url.find('/', parse_offset);
|
||||||
@ -96,7 +96,7 @@ namespace fr
|
|||||||
pos = url.find('?', parse_offset);
|
pos = url.find('?', parse_offset);
|
||||||
if(pos != std::string::npos)
|
if(pos != std::string::npos)
|
||||||
{
|
{
|
||||||
path = url.substr(parse_offset, pos - parse_offset);
|
path.append("/").append(url.substr(parse_offset, pos - parse_offset));
|
||||||
parse_offset = pos + 1;
|
parse_offset = pos + 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -104,7 +104,7 @@ namespace fr
|
|||||||
pos = url.find('#', parse_offset);
|
pos = url.find('#', parse_offset);
|
||||||
pos = (pos != std::string::npos) ? pos : url.find('?', parse_offset);
|
pos = (pos != std::string::npos) ? pos : url.find('?', parse_offset);
|
||||||
pos = (pos != std::string::npos) ? pos : url.size();
|
pos = (pos != std::string::npos) ? pos : url.size();
|
||||||
path = url.substr(parse_offset, pos - parse_offset);
|
path.append("/").append(url.substr(parse_offset, pos - parse_offset));
|
||||||
parse_offset = pos + 1;
|
parse_offset = pos + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
88
tests/NetworkEncodingTest.cpp
Normal file
88
tests/NetworkEncodingTest.cpp
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
//
|
||||||
|
// Created by fred.nicolson on 01/02/18.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include "frnetlib/NetworkEncoding.h"
|
||||||
|
|
||||||
|
constexpr bool is_little_endian()
|
||||||
|
{
|
||||||
|
unsigned short x=0x0001;
|
||||||
|
auto p = reinterpret_cast<unsigned char*>(&x);
|
||||||
|
return *p != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(NetworkEncodingTest, test_htonf)
|
||||||
|
{
|
||||||
|
float input = std::numeric_limits<float>::max();
|
||||||
|
float result = htonf(input);
|
||||||
|
|
||||||
|
if(is_little_endian())
|
||||||
|
{
|
||||||
|
float manual;
|
||||||
|
std::reverse_copy((char*)&input, (char*)&input + sizeof(input), (uint8_t*)&manual);
|
||||||
|
ASSERT_EQ(memcmp(&result, &manual, sizeof(manual)), 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ASSERT_EQ(result, input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(NetworkEncodingTest, test_ntohf)
|
||||||
|
{
|
||||||
|
float input = std::numeric_limits<float>::max();
|
||||||
|
float encoded = htonf(input);
|
||||||
|
float decoded = ntohf(encoded);
|
||||||
|
ASSERT_EQ(input, decoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(NetworkEncodingTest, test_htond)
|
||||||
|
{
|
||||||
|
double input = std::numeric_limits<double>::max();
|
||||||
|
double result = htond(input);
|
||||||
|
|
||||||
|
if(is_little_endian())
|
||||||
|
{
|
||||||
|
double manual;
|
||||||
|
std::reverse_copy((char*)&input, (char*)&input + sizeof(input), (uint8_t*)&manual);
|
||||||
|
ASSERT_EQ(memcmp(&result, &manual, sizeof(manual)), 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ASSERT_EQ(result, input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(NetworkEncodingTest, test_ntohd)
|
||||||
|
{
|
||||||
|
double input = std::numeric_limits<double>::max();
|
||||||
|
double encoded = htond(input);
|
||||||
|
double decoded = ntohd(encoded);
|
||||||
|
ASSERT_EQ(input, decoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(NetworkEncodingTest, test_htonll)
|
||||||
|
{
|
||||||
|
uint64_t input = std::numeric_limits<uint64_t>::max();
|
||||||
|
uint64_t result = htonll(input);
|
||||||
|
|
||||||
|
if(is_little_endian())
|
||||||
|
{
|
||||||
|
uint64_t manual;
|
||||||
|
std::reverse_copy((char*)&input, (char*)&input + sizeof(input), (uint8_t*)&manual);
|
||||||
|
ASSERT_EQ(manual, result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ASSERT_EQ(result, input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(NetworkEncodingTest, test_ntohll)
|
||||||
|
{
|
||||||
|
uint64_t input = std::numeric_limits<uint64_t>::max();
|
||||||
|
uint64_t encoded = htonll(input);
|
||||||
|
uint64_t decoded = ntohll(encoded);
|
||||||
|
ASSERT_EQ(input, decoded);
|
||||||
|
}
|
||||||
@ -10,7 +10,7 @@ TEST(URLTest, full_parse)
|
|||||||
fr::URL url("http://example.com:80/path/path?query=10&bob=20#frag");
|
fr::URL url("http://example.com:80/path/path?query=10&bob=20#frag");
|
||||||
ASSERT_EQ(url.get_host(), "example.com");
|
ASSERT_EQ(url.get_host(), "example.com");
|
||||||
ASSERT_EQ(url.get_scheme(), fr::URL::HTTP);
|
ASSERT_EQ(url.get_scheme(), fr::URL::HTTP);
|
||||||
ASSERT_EQ(url.get_path(), "path/path");
|
ASSERT_EQ(url.get_path(), "/path/path");
|
||||||
ASSERT_EQ(url.get_query(), "query=10&bob=20");
|
ASSERT_EQ(url.get_query(), "query=10&bob=20");
|
||||||
ASSERT_EQ(url.get_fragment(), "frag");
|
ASSERT_EQ(url.get_fragment(), "frag");
|
||||||
}
|
}
|
||||||
@ -38,6 +38,30 @@ TEST(URLTest, fragment_test)
|
|||||||
TEST(URLTest, path_test)
|
TEST(URLTest, path_test)
|
||||||
{
|
{
|
||||||
fr::URL url("example.com/path/hey#frag");
|
fr::URL url("example.com/path/hey#frag");
|
||||||
ASSERT_EQ(url.get_path(), "path/hey");
|
ASSERT_EQ(url.get_path(), "/path/hey");
|
||||||
ASSERT_EQ(url.get_fragment(), "frag");
|
ASSERT_EQ(url.get_fragment(), "frag");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(URLTest, uri_test)
|
||||||
|
{
|
||||||
|
fr::URL url("http://example.com:80/path/path?query=10#frag");
|
||||||
|
ASSERT_EQ(url.get_uri(), "/path/path?query=10#frag");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(URLTest, uri_test2)
|
||||||
|
{
|
||||||
|
fr::URL url("http://example.com:80/path/path?query=10");
|
||||||
|
ASSERT_EQ(url.get_uri(), "/path/path?query=10");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(URLTest, uri_test3)
|
||||||
|
{
|
||||||
|
fr::URL url("http://example.com:80/path/path#frag");
|
||||||
|
ASSERT_EQ(url.get_uri(), "/path/path#frag");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(URLTest, uri_test4)
|
||||||
|
{
|
||||||
|
fr::URL url("http://example.com:80/?bob=10#frag");
|
||||||
|
ASSERT_EQ(url.get_uri(), "/?bob=10#frag");
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user