diff --git a/include/libwebsockets/lws-client.h b/include/libwebsockets/lws-client.h index e7f602d0d..e27bbfa00 100644 --- a/include/libwebsockets/lws-client.h +++ b/include/libwebsockets/lws-client.h @@ -292,4 +292,19 @@ lws_client_http_multipart(struct lws *wsi, const char *name, const char *filename, const char *content_type, char **p, char *end); +/** + * lws_http_basic_auth_gen() - helper to encode client basic auth string + * + * \param user: user name + * \param pw: password + * \param buf: where to store base64 result + * \param len: max usable size of buf + * + * Encodes a username and password in Basic Auth format for use with the + * Authorization header. On return, buf is filled with something like + * "Basic QWxhZGRpbjpPcGVuU2VzYW1l". + */ +LWS_VISIBLE LWS_EXTERN int +lws_http_basic_auth_gen(const char *user, const char *pw, char *buf, size_t len); + ///@} diff --git a/lib/roles/http/client/client-http.c b/lib/roles/http/client/client-http.c index 15ea17170..a1b8176d8 100644 --- a/lib/roles/http/client/client-http.c +++ b/lib/roles/http/client/client-http.c @@ -1290,6 +1290,27 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt) #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) +int +lws_http_basic_auth_gen(const char *user, const char *pw, char *buf, size_t len) +{ + size_t n = strlen(user), m = strlen(pw); + char b[128]; + + if (len < 6 + ((4 * (n + m + 1)) / 3) + 1) + return 1; + + memcpy(buf, "Basic ", 6); + + n = lws_snprintf(b, sizeof(b), "%s:%s", user, pw); + if (n >= sizeof(b) - 2) + return 2; + + lws_b64_encode_string(b, n, buf + 6, len - 6); + buf[len - 1] = '\0'; + + return 0; +} + LWS_VISIBLE int lws_http_client_read(struct lws *wsi, char **buf, int *len) { diff --git a/minimal-examples/http-client/minimal-http-client/README.md b/minimal-examples/http-client/minimal-http-client/README.md index 9baedce5a..9387f8c75 100644 --- a/minimal-examples/http-client/minimal-http-client/README.md +++ b/minimal-examples/http-client/minimal-http-client/README.md @@ -22,6 +22,9 @@ Commandline option|Meaning -m|Apply tls option LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK -e|Apply tls option LCCSCF_ALLOW_EXPIRED -v|Connection validity use 3s / 10s instead of default 5m / 5m10s +--nossl| disable ssl connection +--user | Set Basic Auth username +--password | Set Basic Auth password ``` $ ./lws-minimal-http-client @@ -62,4 +65,12 @@ Commandline option|Meaning [2018/03/04 14:43:23:3042] USER: Completed ``` +You can also test the client Basic Auth support against the http-server/minimal-http-server-basicauth +example. In one console window run the server and in the other + +``` +$ lws-minimal-http-client -l --nossl --path /secret/index.html --user user --password password +``` + +The Basic Auth credentials for the test server are literally username "user" and password "password". diff --git a/minimal-examples/http-client/minimal-http-client/minimal-http-client.c b/minimal-examples/http-client/minimal-http-client/minimal-http-client.c index d5a6e9624..399e302cc 100644 --- a/minimal-examples/http-client/minimal-http-client/minimal-http-client.c +++ b/minimal-examples/http-client/minimal-http-client/minimal-http-client.c @@ -21,6 +21,7 @@ static int interrupted, bad = 1, status; static int long_poll; #endif static struct lws *client_wsi; +static const char *ba_user, *ba_password; static const lws_retry_bo_t retry = { .secs_since_valid_ping = 3, @@ -58,6 +59,24 @@ callback_http(struct lws *wsi, enum lws_callback_reasons reason, #endif break; + /* you only need this if you need to do Basic Auth */ + case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER: + { + unsigned char **p = (unsigned char **)in, *end = (*p) + len; + char b[128]; + + if (!ba_user || !ba_password) + break; + + if (lws_http_basic_auth_gen(ba_user, ba_password, b, sizeof(b))) + break; + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_AUTHORIZATION, + (unsigned char *)b, strlen(b), p, end)) + return -1; + + break; + } + /* chunks of chunked content, with header removed */ case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: lwsl_user("RECEIVE_CLIENT_HTTP_READ: read %d\n", (int)len); @@ -172,12 +191,21 @@ system_notify_cb(lws_state_manager_t *mgr, lws_state_notify_link_t *link, i.address = "warmcat.com"; } + if (lws_cmdline_option(a->argc, a->argv, "--nossl")) + i.ssl_connection = 0; + + if (lws_cmdline_option(a->argc, a->argv, "--h1")) i.alpn = "http/1.1"; if ((p = lws_cmdline_option(a->argc, a->argv, "-p"))) i.port = atoi(p); + if ((p = lws_cmdline_option(a->argc, a->argv, "--user"))) + ba_user = p; + if ((p = lws_cmdline_option(a->argc, a->argv, "--password"))) + ba_password = p; + if (lws_cmdline_option(a->argc, a->argv, "-j")) i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED;