Skip to content

Commit 642ac71

Browse files
authored
Merge pull request #114 from puzza007/ntlm
NTLM support
2 parents 80faed2 + 9504dd5 commit 642ac71

File tree

6 files changed

+66
-17
lines changed

6 files changed

+66
-17
lines changed

README.md

+7-5
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,13 @@ katipo:Method(Pool :: atom(), URL :: binary(), ReqOptions :: map()).
120120
| `unix_socket_path` | `binary()` | `undefined` | [docs](https://curl.haxx.se/libcurl/c/CURLOPT_UNIX_SOCKET_PATH.html) curl >= 7.40.0 |
121121
| `lock_data_ssl_session` | `boolean()` | `false` | [docs](https://curl.haxx.se/libcurl/c/curl_share_setopt.html) curl >= 7.23.0 |
122122
| `doh_url` | `binary()` | `undefined` | [docs](https://curl.haxx.se/libcurl/c/CURLOPT_DOH_URL.html) curl >= 7.62.0 |
123-
| `http_version` | `curl_http_version_none` <br> `curl_http_version_1_0` <br> `curl_http_version_1_1` <br> `curl_http_version_2_0` <br> `curl_http_version_2tls` <br> `curl_http_version_2_prior_knowledge` | `curl_http_version_none` | [docs](https://curl.haxx.se/libcurl/c/CURLOPT_HTTP_VERSION.html) curl >= 7.62.0 |
124-
| `sslcert` | `binary()` | `undefined` | [docs](https://curl.haxx.se/libcurl/c/CURLOPT_SSLCERT.html) |
125-
| `sslkey` | `binary()` | `undefined` | [docs](https://curl.haxx.se/libcurl/c/CURLOPT_SSLKEY.html) |
126-
| `sslkey_blob` | `binary()` (DER format) | `undefined` | [docs](https://curl.haxx.se/libcurl/c/CURLOPT_SSLKEY_BLOB.html) curl >= 7.71.0 |
127-
| `keypasswd` | `binary()` | `undefined` | [docs](https://curl.haxx.se/libcurl/c/CURLOPT_KEYPASSWD.html) |
123+
| `http_version` | `curl_http_version_none` <br> `curl_http_version_1_0` <br> `curl_http_version_1_1` <br> `curl_http_version_2_0` <br> `curl_http_version_2tls` <br> `curl_http_version_2_prior_knowledge` | `curl_http_version_none` | [docs](https://curl.haxx.se/libcurl/c/CURLOPT_HTTP_VERSION.html) curl >= 7.62.0 |
124+
| `sslcert` | `binary()` | `undefined` | [docs](https://curl.haxx.se/libcurl/c/CURLOPT_SSLCERT.html) |
125+
| `sslkey` | `binary()` | `undefined` | [docs](https://curl.haxx.se/libcurl/c/CURLOPT_SSLKEY.html) |
126+
| `sslkey_blob` | `binary()` (DER format) | `undefined` | [docs](https://curl.haxx.se/libcurl/c/CURLOPT_SSLKEY_BLOB.html) curl >= 7.71.0 |
127+
| `keypasswd` | `binary()` | `undefined` | [docs](https://curl.haxx.se/libcurl/c/CURLOPT_KEYPASSWD.html) |
128+
| `http_auth` | `basic | digest | ntlm` | `undefined` | [docs](https://curl.haxx.se/libcurl/c/CURLOPT_HTTPAUTH.html) |
129+
| `userpwd` | `binary()` | `undefined` | [docs](https://curl.haxx.se/libcurl/c/CURLOPT_USERPWD.html) |
128130

129131
#### Responses
130132

c_src/katipo.c

+15
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,12 @@
4545
#define K_CURLOPT_SSLKEY 25
4646
#define K_CURLOPT_SSLKEY_BLOB 26
4747
#define K_CURLOPT_KEYPASSWD 27
48+
#define K_CURLOPT_USERPWD 28
4849

4950
#define K_CURLAUTH_BASIC 100
5051
#define K_CURLAUTH_DIGEST 101
5152
#define K_CURLAUTH_UNDEFINED 102
53+
#define K_CURLAUTH_NTLM 103
5254

5355
struct bufferevent *to_erlang;
5456
struct bufferevent *from_erlang;
@@ -123,6 +125,7 @@ typedef struct _EasyOpts {
123125
char *curlopt_sslkey_blob;
124126
long curlopt_sslkey_blob_size;
125127
char *curlopt_keypasswd;
128+
char *curlopt_userpwd;
126129
} EasyOpts;
127130

128131
static const char *curl_error_code(CURLcode error) {
@@ -867,6 +870,11 @@ static void new_conn(long method, char *url, struct curl_slist *req_headers,
867870
curl_easy_setopt(conn->easy, CURLOPT_KEYPASSWD, "");
868871
}
869872

873+
if (eopts.curlopt_userpwd != NULL) {
874+
curl_easy_setopt(conn->easy, CURLOPT_USERPWD,
875+
eopts.curlopt_userpwd);
876+
}
877+
870878
free(eopts.curlopt_capath);
871879
free(eopts.curlopt_cacert);
872880
free(eopts.curlopt_username);
@@ -879,6 +887,7 @@ static void new_conn(long method, char *url, struct curl_slist *req_headers,
879887
free(eopts.curlopt_sslkey);
880888
free(eopts.curlopt_sslkey_blob);
881889
free(eopts.curlopt_keypasswd);
890+
free(eopts.curlopt_userpwd);
882891

883892
set_method(method, conn);
884893
rc = curl_multi_add_handle(global->multi, conn->easy);
@@ -1046,6 +1055,7 @@ static void erl_input(struct bufferevent *ev, void *arg) {
10461055
eopts.curlopt_sslkey_blob = NULL;
10471056
eopts.curlopt_sslkey_blob_size = 0;
10481057
eopts.curlopt_keypasswd = NULL;
1058+
eopts.curlopt_userpwd = NULL;
10491059

10501060
if (ei_decode_list_header(buf, &index, &num_eopts)) {
10511061
errx(2, "Couldn't decode eopts length");
@@ -1092,6 +1102,8 @@ static void erl_input(struct bufferevent *ev, void *arg) {
10921102
eopts.curlopt_http_auth = CURLAUTH_BASIC;
10931103
} else if (eopt_long == K_CURLAUTH_DIGEST) {
10941104
eopts.curlopt_http_auth = CURLAUTH_DIGEST;
1105+
} else if (eopt_long == K_CURLAUTH_NTLM) {
1106+
eopts.curlopt_http_auth = CURLAUTH_NTLM;
10951107
} else if (eopt_long != K_CURLAUTH_UNDEFINED) {
10961108
errx(2, "Unknown curlopt_http_auth value %ld", eopt_long);
10971109
}
@@ -1153,6 +1165,9 @@ static void erl_input(struct bufferevent *ev, void *arg) {
11531165
case K_CURLOPT_KEYPASSWD:
11541166
eopts.curlopt_keypasswd = eopt_binary;
11551167
break;
1168+
case K_CURLOPT_USERPWD:
1169+
eopts.curlopt_userpwd = eopt_binary;
1170+
break;
11561171
default:
11571172
errx(2, "Unknown eopt binary value %ld", eopt);
11581173
}

rebar.config

+1-2
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,7 @@
4141

4242
{plugins, [rebar3_hex,
4343
rebar3_proper,
44-
{coveralls, "1.4.0"},
45-
geas_rebar3]}.
44+
{coveralls, "1.4.0"}]}.
4645
{cover_enabled, true}.
4746
{cover_export_enabled, true}.
4847
{coveralls_coverdata, "_build/test/cover/ct.coverdata"}.

src/katipo.app.src

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{application, 'katipo',
22
[{description, "HTTP client based on libcurl"},
3-
{vsn, "1.0.1"},
3+
{vsn, "1.0.2"},
44
{registered, []},
55
{mod, {'katipo_app', []}},
66
{applications,

src/katipo.erl

+14-5
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
-define(sslkey, 25).
100100
-define(sslkey_blob, 26).
101101
-define(keypasswd, 27).
102+
-define(userpwd, 28).
102103

103104
-define(DEFAULT_REQ_TIMEOUT, 30000).
104105
-define(FOLLOWLOCATION_TRUE, 1).
@@ -110,6 +111,7 @@
110111
-define(CURLAUTH_BASIC, 100).
111112
-define(CURLAUTH_DIGEST, 101).
112113
-define(CURLAUTH_UNDEFINED, 102).
114+
-define(CURLAUTH_NTLM, 103).
113115
-define(TCP_FASTOPEN_FALSE, 0).
114116
-define(TCP_FASTOPEN_TRUE, 1).
115117
-define(LOCK_DATA_SSL_SESSION_FALSE, 0).
@@ -256,8 +258,8 @@
256258
metrics => proplists:proplist()}} |
257259
{error, #{code := error_code(),
258260
message := error_msg()}}.
259-
-type http_auth() :: basic | digest.
260-
-type http_auth_int() :: ?CURLAUTH_UNDEFINED | ?CURLAUTH_BASIC | ?CURLAUTH_DIGEST.
261+
-type http_auth() :: basic | digest | ntlm.
262+
-type http_auth_int() :: ?CURLAUTH_UNDEFINED | ?CURLAUTH_BASIC | ?CURLAUTH_DIGEST | ?CURLAUTH_NTLM.
261263
-type pipelining() :: nothing | http1 | multiplex.
262264
-type curlopt_http_version() :: curl_http_version_none |
263265
curl_http_version_1_0 |
@@ -316,7 +318,8 @@
316318
sslcert = undefined :: undefined | binary() | file:name_all(),
317319
sslkey = undefined :: undefined | binary() | file:name_all(),
318320
sslkey_blob = undefined :: undefined | binary(),
319-
keypasswd = undefined :: undefined | binary()
321+
keypasswd = undefined :: undefined | binary(),
322+
userpwd = undefined :: undefined | binary()
320323
}).
321324

322325
tcp_fastopen_available() ->
@@ -455,7 +458,8 @@ handle_call(#req{method = Method,
455458
sslcert = SSLCert,
456459
sslkey = SSLKey,
457460
sslkey_blob = SSLKeyBlob,
458-
keypasswd = KeyPasswd},
461+
keypasswd = KeyPasswd,
462+
userpwd = UserPwd},
459463
From,
460464
State=#state{port=Port, reqs=Reqs}) ->
461465
{Self, Ref} = From,
@@ -481,7 +485,8 @@ handle_call(#req{method = Method,
481485
{?sslcert, SSLCert},
482486
{?sslkey, SSLKey},
483487
{?sslkey_blob, SSLKeyBlob},
484-
{?keypasswd, KeyPasswd}],
488+
{?keypasswd, KeyPasswd},
489+
{?userpwd, UserPwd}],
485490
Command = {Self, Ref, Method, Url, Headers, CookieJar, Body, Opts},
486491
true = port_command(Port, term_to_binary(Command)),
487492
Tref = erlang:start_timer(Timeout, self(), {req_timeout, From}),
@@ -656,6 +661,8 @@ opt(http_auth, basic, {Req, Errors}) ->
656661
{Req#req{http_auth=?CURLAUTH_BASIC}, Errors};
657662
opt(http_auth, digest, {Req, Errors}) ->
658663
{Req#req{http_auth=?CURLAUTH_DIGEST}, Errors};
664+
opt(http_auth, ntlm, {Req, Errors}) ->
665+
{Req#req{http_auth=?CURLAUTH_NTLM}, Errors};
659666
opt(username, Username, {Req, Errors}) when is_binary(Username) ->
660667
{Req#req{username=Username}, Errors};
661668
opt(password, Password, {Req, Errors}) when is_binary(Password) ->
@@ -704,6 +711,8 @@ opt(sslkey_blob, Key, {Req, Errors})
704711
{Req#req{sslkey_blob=Key}, Errors};
705712
opt(keypasswd, Pass, {Req, Errors}) when is_binary(Pass) ->
706713
{Req#req{keypasswd=Pass}, Errors};
714+
opt(userpwd, UserPwd, {Req, Errors}) when is_binary(UserPwd) ->
715+
{Req#req{userpwd=UserPwd}, Errors};
707716
opt(K, V, {Req, Errors}) ->
708717
{Req, [{K, V} | Errors]}.
709718

test/katipo_SUITE.erl

+28-4
Original file line numberDiff line numberDiff line change
@@ -121,16 +121,19 @@ groups() ->
121121
unix_socket_path_cant_connect,
122122
timeout_ms,
123123
maxredirs,
124-
basic_unauthorised,
125-
basic_authorised,
126-
digest_unauthorised,
127-
digest_authorised,
128124
lock_data_ssl_session_true,
129125
lock_data_ssl_session_false,
130126
doh_url,
131127
badopts,
132128
proxy_couldnt_connect,
133129
protocol_restriction]},
130+
{digest, [],
131+
[basic_authorised,
132+
basic_authorised_userpwd,
133+
basic_unauthorised,
134+
digest_authorised,
135+
digest_authorised_userpwd,
136+
digest_unauthorised]},
134137
{pool, [],
135138
[pool_start_stop,
136139
worker_death,
@@ -162,6 +165,7 @@ groups() ->
162165

163166
all() ->
164167
[{group, http},
168+
{group, digest},
165169
{group, pool},
166170
{group, https},
167171
{group, https_mutual},
@@ -489,6 +493,16 @@ basic_authorised(_) ->
489493
true = proplists:get_value(<<"authenticated">>, Json),
490494
Username = proplists:get_value(<<"user">>, Json).
491495

496+
basic_authorised_userpwd(_) ->
497+
Username = <<"johndoe">>,
498+
Password = <<"p455w0rd">>,
499+
{ok, #{status := 200, body := Body}} =
500+
katipo:get(?POOL, <<"https://httpbin.org/basic-auth/johndoe/p455w0rd">>,
501+
#{http_auth => basic, userpwd => <<Username/binary,":",Password/binary>>}),
502+
Json = jsx:decode(Body),
503+
true = proplists:get_value(<<"authenticated">>, Json),
504+
Username = proplists:get_value(<<"user">>, Json).
505+
492506
digest_unauthorised(_) ->
493507
{ok, #{status := 401}} =
494508
katipo:get(?POOL, <<"https://httpbin.org/digest-auth/auth/johndoe/p455w0rd">>).
@@ -503,6 +517,16 @@ digest_authorised(_) ->
503517
true = proplists:get_value(<<"authenticated">>, Json),
504518
Username = proplists:get_value(<<"user">>, Json).
505519

520+
digest_authorised_userpwd(_) ->
521+
Username = <<"johndoe">>,
522+
Password = <<"p455w0rd">>,
523+
{ok, #{status := 200, body := Body}} =
524+
katipo:get(?POOL, <<"https://httpbin.org/digest-auth/auth/johndoe/p455w0rd">>,
525+
#{http_auth => digest, userpwd => <<Username/binary,":",Password/binary>>}),
526+
Json = jsx:decode(Body),
527+
true = proplists:get_value(<<"authenticated">>, Json),
528+
Username = proplists:get_value(<<"user">>, Json).
529+
506530
lock_data_ssl_session_true(_) ->
507531
{ok, #{status := 200, body := Body}} =
508532
katipo:get(?POOL, <<"https://httpbin.org/get?a=%21%40%23%24%25%5E%26%2A%28%29_%2B">>,

0 commit comments

Comments
 (0)