@@ -149,9 +149,13 @@ using socket_t = int;
149
149
150
150
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
151
151
#include < openssl/err.h>
152
+ #include < openssl/md5.h>
152
153
#include < openssl/ssl.h>
153
154
#include < openssl/x509v3.h>
154
155
156
+ #include < iomanip>
157
+ #include < sstream>
158
+
155
159
// #if OPENSSL_VERSION_NUMBER < 0x1010100fL
156
160
// #error Sorry, OpenSSL versions prior to 1.1.1 are not supported
157
161
// #endif
@@ -756,10 +760,13 @@ class Client {
756
760
std::vector<Response> &responses);
757
761
758
762
void set_keep_alive_max_count (size_t count);
763
+
759
764
void set_read_timeout (time_t sec, time_t usec);
760
765
761
766
void follow_location (bool on);
762
767
768
+ void set_auth (const char *username, const char *password);
769
+
763
770
protected:
764
771
bool process_request (Stream &strm, const Request &req, Response &res,
765
772
bool last_connection, bool &connection_close);
@@ -772,6 +779,8 @@ class Client {
772
779
time_t read_timeout_sec_;
773
780
time_t read_timeout_usec_;
774
781
size_t follow_location_;
782
+ std::string username_;
783
+ std::string password_;
775
784
776
785
private:
777
786
socket_t create_client_socket () const ;
@@ -1439,6 +1448,7 @@ inline const char *status_message(int status) {
1439
1448
case 303 : return " See Other" ;
1440
1449
case 304 : return " Not Modified" ;
1441
1450
case 400 : return " Bad Request" ;
1451
+ case 401 : return " Unauthorized" ;
1442
1452
case 403 : return " Forbidden" ;
1443
1453
case 404 : return " Not Found" ;
1444
1454
case 413 : return " Payload Too Large" ;
@@ -2287,6 +2297,43 @@ inline bool expect_content(const Request &req) {
2287
2297
return false ;
2288
2298
}
2289
2299
2300
+ #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2301
+ template <typename CTX, typename Init, typename Update, typename Final>
2302
+ inline std::string message_digest (const std::string &s, Init init,
2303
+ Update update, Final final ,
2304
+ size_t digest_length) {
2305
+ using namespace std ;
2306
+
2307
+ unsigned char md[digest_length];
2308
+ CTX ctx;
2309
+ init (&ctx);
2310
+ update (&ctx, s.data (), s.size ());
2311
+ final (md, &ctx);
2312
+
2313
+ stringstream ss;
2314
+ for (auto c : md) {
2315
+ ss << setfill (' 0' ) << setw (2 ) << hex << (unsigned int )c;
2316
+ }
2317
+ return ss.str ();
2318
+ }
2319
+
2320
+ inline std::string MD5 (const std::string &s) {
2321
+ using namespace detail ;
2322
+ return message_digest<MD5_CTX>(s, MD5_Init, MD5_Update, MD5_Final,
2323
+ MD5_DIGEST_LENGTH);
2324
+ }
2325
+
2326
+ inline std::string SHA_256 (const std::string &s) {
2327
+ return message_digest<SHA256_CTX>(s, SHA256_Init, SHA256_Update, SHA256_Final,
2328
+ SHA256_DIGEST_LENGTH);
2329
+ }
2330
+
2331
+ inline std::string SHA_512 (const std::string &s) {
2332
+ return message_digest<SHA512_CTX>(s, SHA512_Init, SHA512_Update, SHA512_Final,
2333
+ SHA512_DIGEST_LENGTH);
2334
+ }
2335
+ #endif
2336
+
2290
2337
#ifdef _WIN32
2291
2338
class WSInit {
2292
2339
public:
@@ -2324,6 +2371,98 @@ make_basic_authentication_header(const std::string &username,
2324
2371
return std::make_pair (" Authorization" , field);
2325
2372
}
2326
2373
2374
+ #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2375
+ inline std::pair<std::string, std::string> make_digest_authentication_header (
2376
+ const Request &req,
2377
+ const std::map<std::string, std::string> &auth,
2378
+ size_t cnonce_count, const std::string &cnonce,
2379
+ const std::string &username, const std::string &password) {
2380
+ using namespace std ;
2381
+
2382
+ string nc;
2383
+ {
2384
+ stringstream ss;
2385
+ ss << setfill (' 0' ) << setw (8 ) << hex << cnonce_count;
2386
+ nc = ss.str ();
2387
+ }
2388
+
2389
+ auto qop = auth.at (" qop" );
2390
+ if (qop.find (" auth-int" ) != std::string::npos) {
2391
+ qop = " auth-int" ;
2392
+ } else {
2393
+ qop = " auth" ;
2394
+ }
2395
+
2396
+ string response;
2397
+ {
2398
+ auto algo = auth.at (" algorithm" );
2399
+
2400
+ auto H = algo == " SHA-256"
2401
+ ? detail::SHA_256
2402
+ : algo == " SHA-512" ? detail::SHA_512 : detail::MD5;
2403
+
2404
+ auto A1 = username + " :" + auth.at (" realm" ) + " :" + password;
2405
+
2406
+ auto A2 = req.method + " :" + req.path ;
2407
+ if (qop == " auth-int" ) {
2408
+ A2 += " :" + H (req.body );
2409
+ }
2410
+
2411
+ response = H (H (A1) + " :" + auth.at (" nonce" ) + " :" + nc + " :" + cnonce +
2412
+ " :" + qop + " :" + H (A2));
2413
+ }
2414
+
2415
+ auto field = " Digest username=\" hello\" , realm=\" " + auth.at (" realm" ) +
2416
+ " \" , nonce=\" " + auth.at (" nonce" ) + " \" , uri=\" " + req.path +
2417
+ " \" , algorithm=" + auth.at (" algorithm" ) + " , qop=" + qop + " , nc=\" " +
2418
+ nc + " \" , cnonce=\" " + cnonce + " \" , response=\" " + response +
2419
+ " \" " ;
2420
+
2421
+ return make_pair (" Authorization" , field);
2422
+ }
2423
+ #endif
2424
+
2425
+ inline int parse_www_authenticate (const httplib::Response &res,
2426
+ std::map<std::string, std::string> &digest_auth) {
2427
+ if (res.has_header (" WWW-Authenticate" )) {
2428
+ static auto re = std::regex (R"~( (?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*)))))~" );
2429
+ auto s = res.get_header_value (" WWW-Authenticate" );
2430
+ auto pos = s.find (' ' );
2431
+ if (pos != std::string::npos) {
2432
+ auto type = s.substr (0 , pos);
2433
+ if (type == " Basic" ) {
2434
+ return 1 ;
2435
+ } else if (type == " Digest" ) {
2436
+ s = s.substr (pos + 1 );
2437
+ auto beg = std::sregex_iterator (s.begin (), s.end (), re);
2438
+ for (auto i = beg; i != std::sregex_iterator (); ++i) {
2439
+ auto m = *i;
2440
+ auto key = s.substr (m.position (1 ), m.length (1 ));
2441
+ auto val = m.length (2 ) > 0 ? s.substr (m.position (2 ), m.length (2 ))
2442
+ : s.substr (m.position (3 ), m.length (3 ));
2443
+ digest_auth[key] = val;
2444
+ }
2445
+ return 2 ;
2446
+ }
2447
+ }
2448
+ }
2449
+ return 0 ;
2450
+ }
2451
+
2452
+ // https://stackoverflow.com/questions/440133/how-do-i-create-a-random-alpha-numeric-string-in-c/440240#answer-440240
2453
+ inline std::string random_string (size_t length) {
2454
+ auto randchar = []() -> char {
2455
+ const char charset[] = " 0123456789"
2456
+ " ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2457
+ " abcdefghijklmnopqrstuvwxyz" ;
2458
+ const size_t max_index = (sizeof (charset) - 1 );
2459
+ return charset[rand () % max_index];
2460
+ };
2461
+ std::string str (length, 0 );
2462
+ std::generate_n (str.begin (), length, randchar);
2463
+ return str;
2464
+ }
2465
+
2327
2466
// Request implementation
2328
2467
inline bool Request::has_header (const char *key) const {
2329
2468
return detail::has_header (headers, key);
@@ -3244,6 +3383,43 @@ inline bool Client::send(const Request &req, Response &res) {
3244
3383
ret = redirect (req, res);
3245
3384
}
3246
3385
3386
+ if (ret && !username_.empty () && !password_.empty () && res.status == 401 ) {
3387
+ int type;
3388
+ std::map<std::string, std::string> digest_auth;
3389
+
3390
+ if ((type = parse_www_authenticate (res, digest_auth)) > 0 ) {
3391
+ std::pair<std::string, std::string> header;
3392
+
3393
+ if (type == 1 ) {
3394
+ header = make_basic_authentication_header (username_, password_);
3395
+ } else if (type == 2 ) {
3396
+ #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
3397
+ size_t cnonce_count = 1 ;
3398
+ auto cnonce = random_string (10 );
3399
+
3400
+ header = make_digest_authentication_header (
3401
+ req, digest_auth, cnonce_count, cnonce, username_, password_);
3402
+ #endif
3403
+ }
3404
+
3405
+ Request new_req;
3406
+ new_req.method = req.method ;
3407
+ new_req.path = req.path ;
3408
+ new_req.headers = req.headers ;
3409
+ new_req.body = req.body ;
3410
+ new_req.response_handler = req.response_handler ;
3411
+ new_req.content_receiver = req.content_receiver ;
3412
+ new_req.progress = req.progress ;
3413
+
3414
+ new_req.headers .insert (header);
3415
+
3416
+ Response new_res;
3417
+ auto ret = send (new_req, new_res);
3418
+ if (ret) { res = new_res; }
3419
+ return ret;
3420
+ }
3421
+ }
3422
+
3247
3423
return ret;
3248
3424
}
3249
3425
@@ -3810,6 +3986,11 @@ inline void Client::set_read_timeout(time_t sec, time_t usec) {
3810
3986
3811
3987
inline void Client::follow_location (bool on) { follow_location_ = on; }
3812
3988
3989
+ inline void Client::set_auth (const char *username, const char *password) {
3990
+ username_ = username;
3991
+ password_ = password;
3992
+ }
3993
+
3813
3994
/*
3814
3995
* SSL Implementation
3815
3996
*/
0 commit comments