Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code deduplication + IPv6 HTTP client fix #2346

Merged
merged 9 commits into from
Aug 21, 2019
11 changes: 2 additions & 9 deletions http/vibe/http/auth/basic_auth.d
Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,8 @@ bool checkBasicAuth(scope HTTPServerRequest req, scope PasswordVerifyCallback pw
return false;
}

static import vibe.http.internal.basic_auth_client;

/**
Augments the given HTTP request with an HTTP Basic Auth header.
*/
void addBasicAuth(scope HTTPRequest req, string user, string password)
denizzzka marked this conversation as resolved.
Show resolved Hide resolved
{
string pwstr = user ~ ":" ~ password;
string authstr = () @trusted { return cast(string)Base64.encode(cast(ubyte[])pwstr); } ();
req.headers["Authorization"] = "Basic " ~ authstr;
}
alias addBasicAuth = vibe.http.internal.basic_auth_client.addBasicAuth;

alias PasswordVerifyCallback = bool delegate(string user, string password);
108 changes: 44 additions & 64 deletions http/vibe/http/client.d
Original file line number Diff line number Diff line change
Expand Up @@ -79,49 +79,12 @@ HTTPClientResponse requestHTTP(URL url, scope void delegate(scope HTTPClientRequ
{
import std.algorithm.searching : canFind;

version(UnixSocket) {
enforce(url.schema == "http" || url.schema == "https" || url.schema == "http+unix" || url.schema == "https+unix", "URL schema must be http(s) or http(s)+unix.");
} else {
enforce(url.schema == "http" || url.schema == "https", "URL schema must be http(s).");
}
enforce(url.host.length > 0, "URL must contain a host name.");
bool use_tls;

if (settings.proxyURL.schema !is null)
use_tls = settings.proxyURL.schema == "https";
else
{
version(UnixSocket)
use_tls = url.schema == "https" || url.schema == "https+unix";
else
use_tls = url.schema == "https";
}
bool use_tls = isTLSRequired(url, settings);

auto cli = connectHTTP(url.getFilteredHost, url.port, use_tls, settings);
auto res = cli.request((req){
if (url.localURI.length) {
assert(url.path.absolute, "Request URL path must be absolute.");
req.requestURL = url.localURI;
}
if (settings.proxyURL.schema !is null)
req.requestURL = url.toString(); // proxy exception to the URL representation

// Provide port number when it is not the default one (RFC2616 section 14.23)
// IPv6 addresses need to be put into brackets
auto hoststr = url.host.canFind(':') ? "["~url.host~"]" : url.host;
if (url.port && url.port != url.defaultPort)
req.headers["Host"] = format("%s:%d", hoststr, url.port);
else
req.headers["Host"] = hoststr;

if ("authorization" !in req.headers && url.username != "") {
import std.base64;
string pwstr = url.username ~ ":" ~ url.password;
req.headers["Authorization"] = "Basic " ~
cast(string)Base64.encode(cast(ubyte[])pwstr);
}
if (requester) requester(req);
});
auto res = cli.request(
(scope req){ httpRequesterDg(req, url, settings, requester); },
);

// make sure the connection stays locked if the body still needs to be read
if( res.m_client ) res.lockedConnection = cli;
Expand All @@ -136,6 +99,19 @@ void requestHTTP(string url, scope void delegate(scope HTTPClientRequest req) re
}
/// ditto
void requestHTTP(URL url, scope void delegate(scope HTTPClientRequest req) requester, scope void delegate(scope HTTPClientResponse req) responder, const(HTTPClientSettings) settings = defaultSettings)
{
bool use_tls = isTLSRequired(url, settings);

auto cli = connectHTTP(url.getFilteredHost, url.port, use_tls, settings);
cli.request(
(scope req){ httpRequesterDg(req, url, settings, requester); },
responder
);
assert(!cli.m_requesting, "HTTP client still requesting after return!?");
assert(!cli.m_responding, "HTTP client still responding after return!?");
}

private bool isTLSRequired(in URL url, in HTTPClientSettings settings)
{
version(UnixSocket) {
enforce(url.schema == "http" || url.schema == "https" || url.schema == "http+unix" || url.schema == "https+unix", "URL schema must be http(s) or http(s)+unix.");
Expand All @@ -155,31 +131,35 @@ void requestHTTP(URL url, scope void delegate(scope HTTPClientRequest req) reque
use_tls = url.schema == "https";
}

auto cli = connectHTTP(url.getFilteredHost, url.port, use_tls, settings);
cli.request((scope req) {
if (url.localURI.length) {
assert(url.path.absolute, "Request URL path must be absolute.");
req.requestURL = url.localURI;
}
if (settings.proxyURL.schema !is null)
req.requestURL = url.toString(); // proxy exception to the URL representation
return use_tls;
}

// Provide port number when it is not the default one (RFC2616 section 14.23)
if (url.port && url.port != url.defaultPort)
req.headers["Host"] = format("%s:%d", url.host, url.port);
else
req.headers["Host"] = url.host;
private void httpRequesterDg(scope HTTPClientRequest req, in URL url, in HTTPClientSettings settings, scope void delegate(scope HTTPClientRequest req) requester)
{
import std.algorithm.searching : canFind;
import vibe.http.internal.basic_auth_client: addBasicAuth;

if ("authorization" !in req.headers && url.username != "") {
import std.base64;
string pwstr = url.username ~ ":" ~ url.password;
req.headers["Authorization"] = "Basic " ~
cast(string)Base64.encode(cast(ubyte[])pwstr);
}
if (requester) requester(req);
}, responder);
assert(!cli.m_requesting, "HTTP client still requesting after return!?");
assert(!cli.m_responding, "HTTP client still responding after return!?");
if (url.localURI.length) {
assert(url.path.absolute, "Request URL path must be absolute.");
req.requestURL = url.localURI;
}

if (settings.proxyURL.schema !is null)
req.requestURL = url.toString(); // proxy exception to the URL representation

// IPv6 addresses need to be put into brackets
auto hoststr = url.host.canFind(':') ? "["~url.host~"]" : url.host;

// Provide port number when it is not the default one (RFC2616 section 14.23)
if (url.port && url.port != url.defaultPort)
req.headers["Host"] = format("%s:%d", hoststr, url.port);
else
req.headers["Host"] = hoststr;

if ("authorization" !in req.headers && url.username != "")
req.addBasicAuth(url.username, url.password);

if (requester) () @trusted { requester(req); } ();
}

/** Posts a simple JSON request. Note that the server www.example.org does not
Expand Down
21 changes: 21 additions & 0 deletions http/vibe/http/internal/basic_auth_client.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
Implements HTTP Basic Auth for client.

License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
*/
module vibe.http.internal.basic_auth_client;

import vibe.http.common;
import std.base64;

@safe:

/**
Augments the given HTTP request with an HTTP Basic Auth header.
*/
void addBasicAuth(scope HTTPRequest req, string user, string password)
{
string pwstr = user ~ ":" ~ password;
string authstr = () @trusted { return cast(string)Base64.encode(cast(ubyte[])pwstr); } ();
req.headers["Authorization"] = "Basic " ~ authstr;
}