Overview
This tutorial will show you how to configure an HTTP client.
We'll start from a very basic loop and add the connection function, and later address error handling.
A basic HTTP client
Let's start with a minimal Mongoose template that initialises and starts an event manager, but does nothing:
#include "mongoose.h"
int main(int argc, char *argv[]) {
struct mg_mgr mgr;
mg_mgr_init(&mgr); // Init manager
for (;;) mg_mgr_poll(&mgr, 1000); // Event loop
mg_mgr_free(&mgr); // Cleanup
return 0;
}
Now, let's create an HTTP client connection before the event loop starts. We will fetch the very first web page in history - a page from CERN, where the Web was invented by Tim Bernes-Lee. That web page is still alive! Let's create a variable that holds a URL:
// First web page in history
static const char *s_url = "http://info.cern.ch/";
Client connection
Now, add a client connection. We need an event handler function for it. That
event handler function is going to get an MG_EV_HTTP_MSG
event when a full
page content gets downloaded.
A client connection is created by the
mg_http_connect() function. An
important aspect of that function is that it only creates a TCP connection
to the server, but does not send a request. The reason for this is that an HTTP
request could be quite complex; therefore it is our responsibility to
construct the request. We do it when we receive an MG_EV_CONNECT
event.
There, we extract a hostname from the URL to form a valid Host
header, which
is mandatory for HTTP/1.1, using the mg_url_host() function.
We also make use of the mg_url_uri() function to point to the URI part in the URL.
Finally, we send a simple GET
request using mg_printf():
#include "mongoose.h"
// First web page in history
static const char *s_url = "http://info.cern.ch/";
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_CONNECT) {
struct mg_str host = mg_url_host(s_url);
// Send request
mg_printf(c,
"GET %s HTTP/1.0\r\n"
"Host: %.*s\r\n"
"\r\n",
mg_url_uri(s_url), (int) host.len, host.ptr);
} if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
printf("%.*s", (int) hm->message.len, hm->message.ptr);
}
}
int main(int argc, char *argv[]) {
struct mg_mgr mgr;
mg_mgr_init(&mgr); // Init manager
mg_http_connect(&mgr, s_url, fn, NULL); // Create client connection
for (;;) mg_mgr_poll(&mgr, 1000); // Event loop
mg_mgr_free(&mgr); // Cleanup
return 0;
}
Error handling
Connection failures will trigger an MG_EV_ERROR
event; an error reason is passed as a char *
pointer in parameter ev_data
.
Check the error handling tutorial for more information
Build and run
A complete example which allows to override a URL via command line flags, handles connection errors, exits when finished, and handles HTTPS URLs too, can be found at examples/http-client
- If you've not already done so, clone the Mongoose Library repo
$ git clone https://github.com/cesanta/mongoose
- Build and run the example
$ cd mongoose/examples/http-client $ make clean all
- Observe the log, you'll see the HTML code of the first web page
To help with verbosity, you can also change the default log level by setting the environment variable LOG_LEVEL to the desired level.
$ export LOG_LEVEL=3 ; ./example
If you like, you can also run the example with a URL of your choice
$ ./example http://somesite
Build with TLS support
The makefile will take care of this, just pass it the proper argument.
For openSSL:
$ make clean all SSL=OPENSSL
For mbedTLS:
$ make clean all SSL=MBEDTLS
If you need to provide extra information, like for example includes and library paths, do it with the EXTRA_CFLAGS variable, like:
$ make clean all SSL=MBEDTLS EXTRA_CFLAGS="-I/path/to/mbedtls/include -L/path/to/mbedtls/lib"
Now you can pass an HTTPS URL to the compiled example.
For more information on building TLS clients, check the TLS tutorial