Network Tutorial
Welcome to the Network Tutorial! In this tutorial, you will learn how to use U++ Core components to enrich your applications with network support. You will also learn how to build server applications that can run locally or be hosted in any public cloud, for example, AWS or Microsoft Azure.
Moreover, the examples attached to this tutorial are bundled with the U++ standard distribution, and they are localized in the tutorial assembly. So, you don't need to rewrite it yourself to launch them or experiment with the code. Good luck!
This is the preview version of the tutorial. All planned content is not yet available.
Table of contents
1. Obtaining data from REST API via HTTP Request
2. Building RESTful server
1. Obtaining data from REST API via HTTP Request
One of the most popular APIs available now on the internet is the REST API. In this tutorial, you will learn how to use the HTTPRequest class to communicate with such an API and use the obtained data directly in the application.
HTTPRequest is a class that performs synchronous or asynchronous HTTP and HTTPS requests. HTTPS requests are very common today. More than 80% of services use this kind of connection. To support HTTPS in your application, you need to add the Core/SSL package to the project. Without this package, your request will fail during runtime.
In this tutorial will use https://restcountries.com publicly available API. This API allows you to explore remote server that holds information about world countries. The API returns data in JSON format. When you open the mentioned page you will have access to the the detailed documentation with all available options. I encourage you to visit it.
For simplification, we will use the /name resource path to search for countries with the given name. In our case, we will look for Germany and information about its name and capital city.
To perform a request, you need to construct an HttpRequest object and pass a URL as a parameter. After constructing the object, there is a need to indicate the HTTP method that should be used. In our case, we will be using GET. To select a method, there is a dedicated method called Method that accepts an integer as a parameter. The integers that can be passed are defined within the HttpRequest class. So, the value to perform a GET request should be HttpRequest::METHOD_GET.
main.cpp
#include <Core/Core.h>
using namespace Upp;
CONSOLE_APP_MAIN {
HttpRequest http(
"https://restcountries.com/v3.1/name/germany&fields=name,capital");
auto content = http.Method(HttpRequest::METHOD_GET).Execute();
if(content.IsVoid()) {
Cout() << "Failed to execute GET request wit error code " << http.GetStatusCode()
<< ".\n";
return;
}
auto json = ParseJSON(content);
if(json.IsError()) {
Cout() << "Failed to parse JSON response.";
return;
}
if(json.GetCount() == 0) {
Cout() << "The JSON is empty. HTTP request returns empty countries list.\n";
return;
}
// Let's parse the search results and display them on the terminal.
Cout() << "Found countries:\n";
for(const auto& result : json) {
String common_name;
String capital;
common_name = result["name"]["common"];
for(const auto& result_capital : result["capital"]) {
capital = result_capital;
// Some countries like South Africa might have more than one capital city. For this
// tutorial, let's ignore it and display only the first result on the list.
break;
}
Cout() << "- " << common_name << " with " << capital << " as a capital city.\n";
}
}
In this case, the status code should be 200 OK, and the JSON response from the server should look as follows:
[{
name : {
common : Germany,
official : Federal Republic of Germany,
nativeName : {deu : {official : Bundesrepublik Deutschland, common : Deutschland}}
},
capital : [Berlin]
}]
Also, please note that the .Method(HttpRequest::METHOD_GET) can be simplified to .GET(). The same applies to other methods as well. For example .Method(HttpRequest::METHOD_POST) can be replaced with .POST(). So the following line of code:
auto content = http.Method(HttpRequest::METHOD_GET).Execute();
Is it exactly the same as:
auto content = http.GET().Execute();
2. Building RESTful server
Now, it's time to build our own RESTful server, and don't look at others. In this tutorial, we will build a simple service with one node /countries that will return predefined countries. We need to start by creating a server that will listen on any port that is not yet taken. To do it, let's create a TcpSocket instance and then call the Listen method. This method, in its simplest form, requires providing a port as an argument. The safe value in this case for development purposes is 8080. Please keep in mind that the default port for HTTP is 80 and for HTTPS is 443. In the production enviromenet, these ports must be used. However, in some operating systems, these ports may be blocked by default, and to unlock them, some additional actions might be needed. So, to save ourselves the trouble, we will use a different port.
After that, we will create a loop for our server logic. In each loop iteration, we will handle one request from the client. To do that, there is a need for the creation of a separate TcpSocket instance dedicated to handling that request. To accept an incoming connection from the client, there is a need to call the socket Accept method. The arugmenet to this method is socket. In our case, we need to pass a server socket. The code for this section is implemented in the RunServerLoop() function in the code sample attached to this paragraph.
In the very final step, we will handle the client request. To do that, we need to read the HTTP header provided by the client. The header contains many valuable pieces of information, such as the URI that was passed by the client and the HTTP method that was used. To obtain the header, let's create an HttpHeader class instance and then call the Read method, with the socket responsible for handling connections to the client as a parameter. In order to handle client requests, we will need to use the following methods of the HttpHeader class:
GetURI() - returns URI associated with the header. We will use this information to process client request. Moreover, URI can also contain additional parameteres to the query.
GetMethod() - returns HTTP method associated with the header.
In the end, to send the response to the client, the HttpResponse function must be used. To use it properly, you need to provide a client socket, a status code, and optionally, if you don't return error content type and data. After providing parameters, the function will return all specified information directly back to the client.
main.cpp
#include <Core/Core.h>
using namespace Upp;
constexpr int SERVER_PORT = 8080;
void ProcessHttpRequest(TcpSocket& client)
{
HttpHeader header;
if(!header.Read(client)) {
Cerr() << "Failed to read HttpHeader.\n";
HttpResponse(client, false, HttpStatus::BAD_REQUEST);
return;
}
auto path = header.GetURI();
if(header.GetMethod() == "GET") {
if(path == "/countries") {
JsonArray ja;
ja << "Czech Republic"
<< "Indonesia"
<< "Brazil"
<< "France";
auto code = HttpStatus::OK;
HttpResponse(client, false, code, HttpStatus::ToString(code), "application/json",
ja.ToString());
}
}
HttpResponse(client, false, HttpStatus::NOT_FOUND);
}
void RunServerLoop(TcpSocket& server)
{
for(;;) {
TcpSocket client;
Cout() << "Waiting for incoming connection from the client...\n";
if(!client.Accept(server)) {
Cerr() << "Connection from the client not accepted.\n";
continue;
}
ProcessHttpRequest(client);
}
}
CONSOLE_APP_MAIN
{
TcpSocket server;
if(!server.Listen(SERVER_PORT)) {
Cerr() << "Cannot open server port for listening with error \"" << server.GetErrorDesc()
<< "\".\n";
return;
}
RunServerLoop(server);
}
In the recent version of the U++ framework (2024.1), the HttpStatus codes were introduced. It means that you don't longer need to provide an explicit status code and prhase to HttpRepsonse. So,
HttpResponse(client, false, HttpStatus::NOT_FOUND);
is the eqivalent to:
HttpResponse(client, false, 404, "Not Found");
To test the above example a curl terminal application can be used. This application is bundled with most Linux distributions, and it can be easily downloaded for Windows. The command that should be run in the terminal is as follows:
curl -v http://127.0.0.1:8080/countries
The output of the command should be:
* Trying 127.0.0.1:8080...
* Connected to 127.0.0.1 (127.0.0.1) port 8080
> GET /countries HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/8.4.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Sun, 3 Dec 2023 12:19:02 +0100
< Server: U++ based server
< Connection: close
< Content-Length: 48
< Content-Type: application/json
<
* Closing connection
["Czech Republic","Indonesia","Brazil","France"]
As you can see in the above output, we received our desirable response. The -v parameter stands for verbose mode, and it is supposed to show additional information. It is very helpful for debugging purposes. Without this parameter, the output is:
["Czech Republic","Indonesia","Brazil","France"]
|