WinINet wrapper - tiny windows HTTPS library, no dependencies.

WNetWrap

A tiny, dependency-free wrapper around WinINet for developers targeting Windows only, who need a lightweight native solution. Inspired by the excellent CPR library, it has mostly identical function names, and will likewise work with random parameter order.

Below is a basic GET request - for detailed examples see the documentation below.

#include <iostream>
#include "wnetwrap.h"

int main()
{	//GET method and firefox user agent used by default
	wrap::Response r = wrap::HttpsRequest(wrap::Url{"https://www.example.com/"}); 
	std::cout << r.text << std::endl; // basic parser
	std::cout << r.status_code << std::endl; // 200
 }

Features

Implemented Upcoming
Custom headers Asynchronous requests
Url encoded parameters Callbacks
Url encoded POST values NTLM authentication
Multipart form POST upload Digest authentication
File POST upload PUT, PATCH and DELETE methods
Basic authentication
Bearer authentication
Connection and request timeout
Cookie support
Proxy support

Usage

Just put wnetwrap.h and wnetwrap.cpp in your project folder. That's it!

Documentation

For now it's all here on the readme, but it will eventually be put on a different page to make navigation more user friendly. To navigate through it use the table of contents dropdown menu.

中文文档稍后会贴在这里但是现在只有英文的,对不起。

GET requests

Making a GET request with WNetWrap is simple - the GET method is used by default so doesn't need to be specified:

#include <wnetwrap.h>
wrap::Response r = wrap::HttpsRequest(wrap::Url{"http://www.httpbin.org/get"});

This gives us a Response object which we’ve called r. There’s a lot of useful stuff in there:

std::cout << r.url << std::endl; // http://www.httpbin.org/get
std::cout << r.status_code << std::endl; // 200
std::cout << r.header["content-type"] << std::endl; // application/json
std::cout << r.text << std::endl;

/*
 * {
 *   "args": {},
 *   "headers": {
 *     ..
 *   },
 *   "url": "http://httpbin.org/get"
 * }
 */

To add URL-encoded parameters, add a Parameters object to the HttpsRequest call:

wrap::Response r = wrap::HttpsRequest(wrap::Url{"http://www.httpbin.org/get"},
                  wrap::Parameters{{"hello", "world"}});
std::cout << r.url << std::endl; // http://www.httpbin.org/get?hello=world
std::cout << r.text << std::endl;

/*
 * {
 *   "args": {
 *     "hello": "world"
 *   },
 *   "headers": {
 *     ..
 *   },
 *   "url": "http://httpbin.org/get?hello=world"
 * }
 */

Parameters is an object with a map-like interface. You can construct it using a list of key/value pairs inside the HttpsRequest or have it outlive HttpsRequest by constructing it outside:

// Constructing it in place
wrap::Response r = wrap::HttpsRequest(wrap::Url{"http://www.httpbin.org/get"},
                  wrap::Parameters{{"hello", "world"}, {"stay", "cool"}});
std::cout << r.url << std::endl; // http://www.httpbin.org/get?hello=world&stay=cool
std::cout << r.text << std::endl;

/*
 * {
 *   "args": {
 *     "hello": "world"
 *     "stay": "cool"
 *   },
 *   "headers": {
 *     ..
 *   },
 *   "url": "http://httpbin.org/get?hello=world&stay=cool"
 * }
 */

 // Constructing it outside
wrap::Parameters parameters = wrap::Parameters{{"hello", "world"}, {"stay", "cool"}};
wrap::Response r_outside = wrap::HttpsRequest(wrap::Url{"http://www.httpbin.org/get"}, parameters);
std::cout << r_outside.url << std::endl; // http://www.httpbin.org/get?hello=world&stay=cool
std::cout << r_outside.text << std::endl; // Same text response as above

Downloading a file

To download the contents of the request you simply add a Download parameter to HttpsRequest. If this parameter's value is blank then the file is downloaded with its original filename, otherwise the value provided will be the new file's name. For example, to download the CPR library:

wrap::HttpsRequest(wrap::Url{ "https://github.com/whoshuu/cpr/archive/refs/tags/1.6.0.zip" }, wrap::Download{});

When you download a file, the .raw and .text properties of the response object will be returned empty.

POST Requests

Making a POST request is just a matter of specifying the HTTP method:

wrap::Response r = wrap::HttpsRequest(wrap::Url{"http://www.httpbin.org/post"},
                   wrap::Payload{{"key", "value"}}, 
		   wrap::Method{"POST"});
std::cout << r.text << std::endl;

/*
 * {
 *   "args": {},
 *   "data": "",
 *   "files": {},
 *   "form": {
 *     "key": "value"
 *   },
 *   "headers": {
 *     ..
 *     "Content-Type": "application/x-www-form-urlencoded",
 *     ..
 *   },
 *   "json": null,
 *   "url": "http://www.httpbin.org/post"
 * }
 */

This sends up "key=value" as a "x-www-form-urlencoded" pair in the POST request. To send data raw and unencoded, use Body instead of Payload:

wrap::Response r = wrap::HttpsRequest(wrap::Url{"http://www.httpbin.org/post"},
                   wrap::Body{"This is raw POST data"},
                   wrap::Header{{"Content-Type", "text/plain"}},
		   wrap::Method{"POST"});
std::cout << r.text << std::endl;

/*
 * {
 *   "args": {},
 *   "data": "This is raw POST data",
 *   "files": {},
 *   "form": {},
 *   "headers": {
 *     ..
 *     "Content-Type": "text/plain",
 *     ..
 *   },
 *   "json": null,
 *   "url": "http://www.httpbin.org/post"
 * }
 */

Here you will notice that the "Content-Type" is being set explicitly to "text/plain". This is because by default, "x-www-form-urlencoded" is used for raw data POSTs. For cases where the data being sent up is small, either "x-www-form-urlencoded" or "text/plain" is suitable. If the data package is large or contains a file, it’s more appropriate to use a Multipart upload. In this example we are uploading a textfile to file.io:

wrap::Response r = wrap::HttpsRequest(wrap::Url{ "file.io" },
				      wrap::Multipart{ {"file:sample1","sample.txt"} }, 
			              wrap::Method{ "POST" });
std::cout << r.text << std::endl;

/*
{"success":true,"status":200,"id":"0a1dc4a0-d056-11eb-b8a8-95e106f75f99","key":"JBDaFwjAneQH","name":"sample.txt","link":"https://
file.io/JBDaFwjAneQH","private":false,"expires":"2021-07-02T16:55:52.042Z","downloads":0,"maxDownloads":1,"autoDelete":true,"size"
:53,"mimeType":"text/plain","created":"2021-06-18T16:55:52.042Z","modified":"2021-06-18T16:55:52.042Z"}
 */

Notice how the text file, which in this case was passed as sample1, had file: prefixed before it - this tells WNetWrap that this is a file and not a key - value pair.

Authentication

To use Basic Authentication which uses a username and password, just add Authentication to the call:

wrap::Response r = wrap::HttpsRequest(wrap::Url{"http://www.httpbin.org/basic-auth/user/pass"},
                  wrap::Authentication{"user", "pass"});
std::cout << r.text << std::endl;

/*
 * {
 *   "authenticated": true,
 *   "user": "user"
 * }
 */

Authentication via an OAuth - Bearer Token can be done using the Bearer authentication object:

wrap::Response r = wrap::HttpsRequest(wrap::Url{"http://www.httpbin.org/bearer"},
                  wrap::Bearer{"ACCESS_TOKEN"});
std::cout << r.text << std::endl;

/*
 * {
 *   "authenticated": true,
 *   "token": "ACCESS_TOKEN"
 * }
 */

Response Objects

A Response has these public fields and methods:

std::string status_code;        // The HTTP status code for the request
std::string raw;                // The body of the HTTP response
std::string text;               // The text body in case of HTML response - if not HTML, same as raw above
std::map header;                // A map of the header fields received
std::map sent_headers;          // A map of the headers sent
std::map secinfo;               // A map of certificate information strings (HTTPS only)
std::string url;                // The effective URL of the ultimate request
std::string err;                // An error string containing the error code and a message
unsigned long uploaded_bytes;   // How many bytes have been sent to the server
unsigned long downloaded_bytes; // How many bytes have been received from the server
unsigned long redirect_count;   // How many redirects occurred

The header is a map with an important modification. Its keys are case insensitive as required by RFC 7230:

wrap::Response r = wrap::HttpsRequest(wrap::Url{"http://www.httpbin.org/get"});
std::cout << r.header["content-type"] << std::endl;
std::cout << r.header["Content-Type"] << std::endl;
std::cout << r.header["CoNtEnT-tYpE"] << std::endl;

All of these should print the same value, "application/json".

Request Headers

Using Header in your HttpsRequest you can specify custom headers:

wrap::Response r = wrap::HttpsRequest(wrap::Url{"http://www.httpbin.org/headers"},
                  wrap::Header{{"accept", "application/json"}});
std::cout << r.text << std::endl;

/*
 * "headers": {
 *   "Accept": "application/json",
 *   "Host": "www.httpbin.org",
 *   "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0"
 * }
 */

Setting Timeouts

It’s possible to set a timeout for your request if you have strict timing requirements:

wrap::Response r = wrap::HttpsRequest(wrap::Url{"http://www.httpbin.org/get"},
                  wrap::Timeout{1000}); // will timeout after 1000 ms

Setting the Timeout option sets the maximum allowed time the connection or request operation can take in milliseconds. By default a Timeout will only apply to the request itself, but you can specify either one by adding either connection or request, or both with all:

wrap::Timeout{1000,"connection"}

Since WNetWrap is built on top of WinINet, it’s important to know what setting this Timeout does to the request. It creates a worker thread which executes the connection or request call. This thread is then monitored and killed if it takes longer than the timeout specified. The reason this approach is taken is that the normal method of setting a timeout with WinINet does not work, due to a 20+ year old MS bug. You can find out more about this workaround here. What it means in practical terms is that Timeout cannot be set to a value higher than WinINet's default (currently 1 hour).

Using Proxies

Setting up a proxy is easy:

wrap::Response r = wrap::HttpsRequest(wrap::Url{"http://www.httpbin.org/get"},
                  wrap::Proxy{"http://www.fakeproxy.com"});
std::cout << r.url << std::endl; // Prints http://www.httpbin.org/get, not the proxy url

You have the option of specifying a username and password for the proxy:

wrap::Response r = wrap::HttpsRequest(wrap::Url{"http://www.httpbin.org/get"},
                  wrap::Proxy{"http://www.fakeproxy.com", "UserName" , "p455w0rd!"});

Sending & Receiving Cookies

Accessing received cookies is done like this:

wrap::Response r = wrap::HttpsRequest(wrap::Url{"http://www.httpbin.org/cookies/set?cookies=yummy"});
std::cout << r.cookies["cookies"] << std::endl; // Prints yummy
std::cout << r.cookies["Cookies"] << std::endl; // Prints nothing

To send new cookies just use Cookies in the request:

wrap::Response r = wrap::HttpsRequest(wrap::Url{"http://www.httpbin.org/cookies"},
                  wrap::Cookies{{"ice cream", "is delicious"}});
std::cout << r.text << std::endl;

/*
 * {
 *   "cookies": {
 *     "ice%20cream": "is%20delicious"
 *   }
 * }
 */

By default Cookies and their values will be URL-encoded. Although this is recommend, it is not mandatory for Cookies to be URL-encoded.

[...] To maximize compatibility with user agents, servers that wish to store arbitrary data in a cookie-value SHOULD encode that data, for example, using Base64 [RFC4648]. [...]

Source: RFC6265

URL-encoding for Cookies can be disabled by setting encode cookies to false or off in the Options constructor (see more on Options below).

wrap::Response r = wrap::HttpsRequest(wrap::Url{"http://www.httpbin.org/cookies"},
                  wrap::Cookies{{"ice cream", "is delicious"}}, Options{ {"encode cookies","off"}});
std::cout << r.text << std::endl;

/*
 * {
 *   "cookies": {
 *     "ice cream": "is delicious"
 *   }
 * }
 */

Security Certificate Info

To see the response's security info, you will need to access the secinfo map. For example, to get the security certificate:

r.secinfo["certificate"]

For www.example.com this returns:

Subject:
US
California
Los Angeles
Internet Corporation for Assigned Names and Numbers
www.example.org
Issuer:
US
DigiCert Inc
DigiCert TLS RSA SHA256 2020 CA1
Effective Date: 24/11/2020 00:00:00
Expiration Date:        25/12/2021 23:59:59
Security Protocol:      (null)
Signature Type: (null)
Encryption Type:        (null)
Privacy Strength:       High (128 bits)
cipher : AES 128-bit encryption algorithm

Due to WinInet limitations, some data such as the protocol and encryption type may appear as (null) - however this may be found in other parts of the certificate, such as under Issuer above. This can also be found as one of several additional elements in the secinfo map:

cout << r.secinfo["protocol"]; // example.com: Transport Layer Security 1.2 client-side 

Cycling through the secinfo map will show all other available security info:

cout << "security info:" << endl;
for (auto elem : r.secinfo)
{
	cout << elem.first + " : " + elem.second + "\r\n";
}

This gives the map keys and values (I've omitted the certificate):

cipher : AES 128-bit encryption algorithm
cipher_strength : 128
hash : SHA hashing algorithm
hash_strength : 128
key_exch : RSA key exchange
key_exch_strength : 2048
protocol : Transport Layer Security 1.2 client-side

Additional Options

The Options constructor allows you to specify additional options for the request. For example, to turn off redirects (which are on by default and handled automatically by WNetWrap):

wrap::HttpsRequest(wrap::Url{"http://www.httpbin.org/get"}, wrap::Options{"redirect" , "off"});

To enable the option you can use either on or true, and to disable either false or off will do.

Similar Resources

HTTP/HTTPS REST Client C Library

https_client HTTP/HTTPS REST Client C Library This library is a tiny https client library. it use only small memory(default read buffer size(H_READ_SI

Dec 20, 2022

A fork of Endless Sky for playing in a browser: try it at https://play-endless-sky.com/

Endless Web A fork of Endless Sky to make the game playable in a browser. Play at https://play-endless-web.com File issues for anything to do with the

Oct 19, 2022

cuehttp is a modern c++ middleware framework for http(http/https)/websocket(ws/wss).

cuehttp 简介 cuehttp是一个使用Modern C++(C++17)编写的跨平台、高性能、易用的HTTP/WebSocket框架。基于中间件模式可以方便、高效、优雅的增加功能。cuehttp基于boost.asio开发,使用picohttpparser进行HTTP协议解析。内部依赖了nl

Dec 17, 2022

Dohd is a minimalist DNS-over-HTTPS daemon that redirects all DoH queries to a local DNS server running on localhost:53 (UDP)

dohd Dohd (pron. doh-dee) is a minimalist DNS-over-HTTPS daemon that redirects all DoH queries to a local DNS server running on localhost:53 (UDP). Fe

Dec 1, 2022

Wrapper library for the BSD sockets API with a nicer C99 interface

A wrapper library for the BSD sockets API. Why? This library trades the series of getaddrinfo, socket, connect, bind, listen, etc. functions and their

Dec 10, 2022

C++ wrapper for librg network library

librg-cpp Description C++ wrapper for librg network library Warning: The wrapper is under heavy development and will have breaking changes still a fir

Sep 28, 2020

tiny HTTP parser written in C (used in HTTP::Parser::XS et al.)

tiny HTTP parser written in C (used in HTTP::Parser::XS et al.)

PicoHTTPParser Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase, Shigeo Mitsunari PicoHTTPParser is a tiny, primitive, fast HTTP r

Jan 1, 2023

Pipy is a tiny, high performance, highly stable, programmable proxy written in C++

Pipy is a tiny, high performance, highly stable, programmable proxy. Written in C++, built on top of Asio asynchronous I/O library, Pipy is extremely lightweight and fast, making it one of the best choices for service mesh sidecars.

Dec 23, 2022

A tiny example how to work with ZigBee stack using JN5169 microcontroller

A tiny example how to work with ZigBee stack using JN5169 microcontroller

Hello NXP JN5169 ZigBee World This is a tiny example how to work with ZigBee stack using JN5169 microcontroller. The example implements a smart switch

Jan 1, 2023
Comments
  • HTTP requests support

    HTTP requests support

    Normal http requests (no ssl) does not seem to work.

    The error returned by GetLastError() is 12157, which translate into:

    ERROR_INTERNET_SECURITY_CHANNEL_ERROR

    The application experienced an internal error loading the SSL libraries.

    This is probably due to INTERNET_SECURE_FLAG used in HttpOpenRequestA().

    A simple solution could be to select flags based on which type of protocol is used.

  • Improve error handling

    Improve error handling

    Error handling right now is just a text output of GetLastError() in the response object, and it doesn't cover all possible errors. There should be a more helpful error handling function for the whole library.

Header-only, event based, tiny and easy to use libuv wrapper in modern C++ - now available as also shared/static library!

Do you have a question that doesn't require you to open an issue? Join the gitter channel. If you use uvw and you want to say thanks or support the pr

Jan 9, 2023
A lightweight Universal Windows proxy app based on https://github.com/eycorsican/leaf
A lightweight Universal Windows proxy app based on https://github.com/eycorsican/leaf

Maple A lightweight Universal Windows proxy app based on https://github.com/eycorsican/leaf Features Configuration management Outbound network adapter

Jan 6, 2023
Graphical small-internet client for windows, linux, MacOS X and BSDs. Supports gemini, http, https, gopher, finger.
Graphical small-internet client for windows, linux, MacOS X and BSDs. Supports gemini, http, https, gopher, finger.

Graphical small-internet client for windows, linux, MacOS X and BSDs. Supports gemini, http, https, gopher, finger.

Dec 30, 2022
WARFOX is a software-based HTTPS beaconing Windows implant that uses a multi-layered proxy network for C2 communications.
WARFOX is a software-based HTTPS beaconing Windows implant that uses a multi-layered proxy network for C2 communications.

An HTTPS beaconing Windows implant and multi-layered proxy C2 network designed for covert APT emulation focused offensive operations

Nov 25, 2022
An HTTPS beaconing Windows implant and multi-layered proxy C2 network designed for covert APT emulation focused offensive operations
An HTTPS beaconing Windows implant and multi-layered proxy C2 network designed for covert APT emulation focused offensive operations

WARFOX is a software-based HTTPS beaconing Windows implant that uses a multi-layered proxy network for C2 communications. This kit was designed to emulate covert APT offensive operations. This kit includes WARFOX (Windows implant), HIGHTOWER (Listening Post), and other tools to build configs and set up a proxy network.

Nov 25, 2022
Get fresh version of openssl using prefab dependencies!

OpenSSL static library + prefab Easy to use solution to bake fresh version of OpenSLL into your NDK Library Before you start This package made for usi

Dec 10, 2021
A C++ header-only HTTP/HTTPS server and client library
A C++ header-only HTTP/HTTPS server and client library

cpp-httplib A C++11 single-file header-only cross platform HTTP/HTTPS library. It's extremely easy to setup. Just include the httplib.h file in your c

Dec 31, 2022
Ultra fast and low latency asynchronous socket server & client C++ library with support TCP, SSL, UDP, HTTP, HTTPS, WebSocket protocols and 10K connections problem solution
Ultra fast and low latency asynchronous socket server & client C++ library with support TCP, SSL, UDP, HTTP, HTTPS, WebSocket protocols and 10K connections problem solution

CppServer Ultra fast and low latency asynchronous socket server & client C++ library with support TCP, SSL, UDP, HTTP, HTTPS, WebSocket protocols and

Jan 3, 2023
Dec 15, 2022
A very simple, fast, multithreaded, platform independent HTTP and HTTPS server and client library implemented using C++11 and Boost.Asio.

A very simple, fast, multithreaded, platform independent HTTP and HTTPS server and client library implemented using C++11 and Boost.Asio. Created to be an easy way to make REST resources available from C++ applications.

Dec 23, 2022