mirror of
https://github.com/libgit2/libgit2.git
synced 2026-01-25 02:56:17 +00:00
http: allow users more control over user-agent
Users can now override the "product" portion of the user-agent (via GIT_OPT_SET_USER_AGENT_PRODUCT). This continues to default to "git/2.0", but users may define their own string, or may opt out of sending a user-agent entirely (by passing an empty string). Similarly, users may now also opt-out of sending any additional "comment" information by setting the GIT_OPT_SET_USER_AGENT value to an empty string.
This commit is contained in:
@@ -228,7 +228,9 @@ typedef enum {
|
||||
GIT_OPT_SET_SERVER_CONNECT_TIMEOUT,
|
||||
GIT_OPT_GET_SERVER_CONNECT_TIMEOUT,
|
||||
GIT_OPT_SET_SERVER_TIMEOUT,
|
||||
GIT_OPT_GET_SERVER_TIMEOUT
|
||||
GIT_OPT_GET_SERVER_TIMEOUT,
|
||||
GIT_OPT_SET_USER_AGENT_PRODUCT,
|
||||
GIT_OPT_GET_USER_AGENT_PRODUCT
|
||||
} git_libgit2_opt_t;
|
||||
|
||||
/**
|
||||
@@ -337,11 +339,35 @@ typedef enum {
|
||||
*
|
||||
* * opts(GIT_OPT_SET_USER_AGENT, const char *user_agent)
|
||||
*
|
||||
* > Set the value of the User-Agent header. This value will be
|
||||
* > appended to "git/1.0", for compatibility with other git clients.
|
||||
* > Set the value of the comment section of the User-Agent header.
|
||||
* > This can be information about your product and its version.
|
||||
* > By default this is "libgit2" followed by the libgit2 version.
|
||||
* >
|
||||
* > - `user_agent` is the value that will be delivered as the
|
||||
* > User-Agent header on HTTP requests.
|
||||
* > This value will be appended to User-Agent _product_, which
|
||||
* > is typically set to "git/2.0".
|
||||
* >
|
||||
* > Set to the empty string ("") to not send any information in the
|
||||
* > comment section, or set to NULL to restore the default.
|
||||
*
|
||||
* * opts(GIT_OPT_GET_USER_AGENT, git_buf *out)
|
||||
*
|
||||
* > Get the value of the User-Agent header.
|
||||
* > The User-Agent is written to the `out` buffer.
|
||||
*
|
||||
* * opts(GIT_OPT_SET_USER_AGENT_PRODUCT, const char *user_agent_product)
|
||||
*
|
||||
* > Set the value of the product portion of the User-Agent header.
|
||||
* > This defaults to "git/2.0", for compatibility with other git
|
||||
* > clients. It is recommended to keep this as git/<version> for
|
||||
* > compatibility with servers that do user-agent detection.
|
||||
* >
|
||||
* > Set to the empty string ("") to not send any user-agent string,
|
||||
* > or set to NULL to restore the default.
|
||||
*
|
||||
* * opts(GIT_OPT_GET_USER_AGENT_PRODUCT, git_buf *out)
|
||||
*
|
||||
* > Get the value of the User-Agent product header.
|
||||
* > The User-Agent product is written to the `out` buffer.
|
||||
*
|
||||
* * opts(GIT_OPT_SET_WINDOWS_SHAREMODE, unsigned long value)
|
||||
*
|
||||
@@ -377,11 +403,6 @@ typedef enum {
|
||||
* >
|
||||
* > - `ciphers` is the list of ciphers that are eanbled.
|
||||
*
|
||||
* * opts(GIT_OPT_GET_USER_AGENT, git_buf *out)
|
||||
*
|
||||
* > Get the value of the User-Agent header.
|
||||
* > The User-Agent is written to the `out` buffer.
|
||||
*
|
||||
* * opts(GIT_OPT_ENABLE_OFS_DELTA, int enabled)
|
||||
*
|
||||
* > Enable or disable the use of "offset deltas" when creating packfiles,
|
||||
|
||||
@@ -51,11 +51,14 @@ extern int git_socket_stream__connect_timeout;
|
||||
extern int git_socket_stream__timeout;
|
||||
|
||||
char *git__user_agent;
|
||||
char *git__user_agent_product;
|
||||
char *git__ssl_ciphers;
|
||||
|
||||
static void settings_global_shutdown(void)
|
||||
{
|
||||
git__free(git__user_agent);
|
||||
git__free(git__user_agent_product);
|
||||
|
||||
git__free(git__ssl_ciphers);
|
||||
git_repository__free_extensions();
|
||||
}
|
||||
@@ -89,9 +92,16 @@ static int config_level_to_sysdir(int *out, int config_level)
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *git_settings__user_agent_product(void)
|
||||
{
|
||||
return git__user_agent_product ? git__user_agent_product :
|
||||
"git/2.0";
|
||||
}
|
||||
|
||||
const char *git_settings__user_agent(void)
|
||||
{
|
||||
return git__user_agent;
|
||||
return git__user_agent ? git__user_agent :
|
||||
"libgit2 " LIBGIT2_VERSION;
|
||||
}
|
||||
|
||||
int git_libgit2_opts(int key, ...)
|
||||
@@ -211,14 +221,65 @@ int git_libgit2_opts(int key, ...)
|
||||
error = -1;
|
||||
#endif
|
||||
break;
|
||||
case GIT_OPT_SET_USER_AGENT:
|
||||
git__free(git__user_agent);
|
||||
git__user_agent = git__strdup(va_arg(ap, const char *));
|
||||
if (!git__user_agent) {
|
||||
git_error_set_oom();
|
||||
error = -1;
|
||||
}
|
||||
|
||||
case GIT_OPT_SET_USER_AGENT:
|
||||
{
|
||||
const char *new_agent = va_arg(ap, const char *);
|
||||
|
||||
git__free(git__user_agent);
|
||||
|
||||
if (new_agent) {
|
||||
git__user_agent= git__strdup(new_agent);
|
||||
|
||||
if (!git__user_agent)
|
||||
error = -1;
|
||||
} else {
|
||||
git__user_agent = NULL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case GIT_OPT_GET_USER_AGENT:
|
||||
{
|
||||
git_buf *out = va_arg(ap, git_buf *);
|
||||
git_str str = GIT_STR_INIT;
|
||||
|
||||
if ((error = git_buf_tostr(&str, out)) < 0 ||
|
||||
(error = git_str_puts(&str, git_settings__user_agent())) < 0)
|
||||
break;
|
||||
|
||||
error = git_buf_fromstr(out, &str);
|
||||
}
|
||||
break;
|
||||
|
||||
case GIT_OPT_SET_USER_AGENT_PRODUCT:
|
||||
{
|
||||
const char *new_agent = va_arg(ap, const char *);
|
||||
|
||||
git__free(git__user_agent_product);
|
||||
|
||||
if (new_agent) {
|
||||
git__user_agent_product = git__strdup(new_agent);
|
||||
|
||||
if (!git__user_agent_product)
|
||||
error = -1;
|
||||
} else {
|
||||
git__user_agent_product = NULL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case GIT_OPT_GET_USER_AGENT_PRODUCT:
|
||||
{
|
||||
git_buf *out = va_arg(ap, git_buf *);
|
||||
git_str str = GIT_STR_INIT;
|
||||
|
||||
if ((error = git_buf_tostr(&str, out)) < 0 ||
|
||||
(error = git_str_puts(&str, git_settings__user_agent_product())) < 0)
|
||||
break;
|
||||
|
||||
error = git_buf_fromstr(out, &str);
|
||||
}
|
||||
break;
|
||||
|
||||
case GIT_OPT_ENABLE_STRICT_OBJECT_CREATION:
|
||||
@@ -245,19 +306,6 @@ int git_libgit2_opts(int key, ...)
|
||||
#endif
|
||||
break;
|
||||
|
||||
case GIT_OPT_GET_USER_AGENT:
|
||||
{
|
||||
git_buf *out = va_arg(ap, git_buf *);
|
||||
git_str str = GIT_STR_INIT;
|
||||
|
||||
if ((error = git_buf_tostr(&str, out)) < 0 ||
|
||||
(error = git_str_puts(&str, git__user_agent)) < 0)
|
||||
break;
|
||||
|
||||
error = git_buf_fromstr(out, &str);
|
||||
}
|
||||
break;
|
||||
|
||||
case GIT_OPT_ENABLE_OFS_DELTA:
|
||||
git_smart__ofs_delta_enabled = (va_arg(ap, int) != 0);
|
||||
break;
|
||||
|
||||
@@ -10,5 +10,6 @@
|
||||
extern int git_settings_global_init(void);
|
||||
|
||||
extern const char *git_settings__user_agent(void);
|
||||
extern const char *git_settings__user_agent_product(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -15,14 +15,4 @@
|
||||
|
||||
extern bool git_http__expect_continue;
|
||||
|
||||
GIT_INLINE(int) git_http__user_agent(git_str *buf)
|
||||
{
|
||||
const char *ua = git_settings__user_agent();
|
||||
|
||||
if (!ua)
|
||||
ua = "libgit2 " LIBGIT2_VERSION;
|
||||
|
||||
return git_str_printf(buf, "git/2.0 (%s)", ua);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -651,6 +651,30 @@ static int puts_host_and_port(git_str *buf, git_net_url *url, bool force_port)
|
||||
return git_str_oom(buf) ? -1 : 0;
|
||||
}
|
||||
|
||||
static int append_user_agent(git_str *buf)
|
||||
{
|
||||
const char *product = git_settings__user_agent_product();
|
||||
const char *comment = git_settings__user_agent();
|
||||
|
||||
GIT_ASSERT(product && comment);
|
||||
|
||||
if (!*product)
|
||||
return 0;
|
||||
|
||||
git_str_puts(buf, "User-Agent: ");
|
||||
git_str_puts(buf, product);
|
||||
|
||||
if (*comment) {
|
||||
git_str_puts(buf, " (");
|
||||
git_str_puts(buf, comment);
|
||||
git_str_puts(buf, ")");
|
||||
}
|
||||
|
||||
git_str_puts(buf, "\r\n");
|
||||
|
||||
return git_str_oom(buf) ? -1 : 0;
|
||||
}
|
||||
|
||||
static int generate_connect_request(
|
||||
git_http_client *client,
|
||||
git_http_request *request)
|
||||
@@ -665,9 +689,7 @@ static int generate_connect_request(
|
||||
puts_host_and_port(buf, &client->server.url, true);
|
||||
git_str_puts(buf, " HTTP/1.1\r\n");
|
||||
|
||||
git_str_puts(buf, "User-Agent: ");
|
||||
git_http__user_agent(buf);
|
||||
git_str_puts(buf, "\r\n");
|
||||
append_user_agent(buf);
|
||||
|
||||
git_str_puts(buf, "Host: ");
|
||||
puts_host_and_port(buf, &client->server.url, true);
|
||||
@@ -711,9 +733,7 @@ static int generate_request(
|
||||
|
||||
git_str_puts(buf, " HTTP/1.1\r\n");
|
||||
|
||||
git_str_puts(buf, "User-Agent: ");
|
||||
git_http__user_agent(buf);
|
||||
git_str_puts(buf, "\r\n");
|
||||
append_user_agent(buf);
|
||||
|
||||
git_str_puts(buf, "Host: ");
|
||||
puts_host_and_port(buf, request->url, false);
|
||||
|
||||
@@ -746,6 +746,33 @@ static void CALLBACK winhttp_status(
|
||||
}
|
||||
}
|
||||
|
||||
static int user_agent(bool *exists, git_str *out)
|
||||
{
|
||||
const char *product = git_settings__user_agent_product();
|
||||
const char *comment = git_settings__user_agent();
|
||||
|
||||
GIT_ASSERT(product && comment);
|
||||
|
||||
if (!*product) {
|
||||
*exists = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
git_str_puts(out, product);
|
||||
|
||||
if (*comment) {
|
||||
git_str_puts(out, " (");
|
||||
git_str_puts(out, comment);
|
||||
git_str_puts(out, ")");
|
||||
}
|
||||
|
||||
if (git_str_oom(out))
|
||||
return -1;
|
||||
|
||||
*exists = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int winhttp_connect(
|
||||
winhttp_subtransport *t)
|
||||
{
|
||||
@@ -757,6 +784,7 @@ static int winhttp_connect(
|
||||
int error = -1;
|
||||
int default_timeout = TIMEOUT_INFINITE;
|
||||
int default_connect_timeout = DEFAULT_CONNECT_TIMEOUT;
|
||||
bool has_ua = true;
|
||||
DWORD protocols =
|
||||
WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 |
|
||||
WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 |
|
||||
@@ -787,11 +815,11 @@ static int winhttp_connect(
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
|
||||
if (git_http__user_agent(&ua) < 0)
|
||||
if (user_agent(&has_ua, &ua) < 0)
|
||||
goto on_error;
|
||||
|
||||
if (git_utf8_to_16_alloc(&wide_ua, git_str_cstr(&ua)) < 0) {
|
||||
if (has_ua &&
|
||||
git_utf8_to_16_alloc(&wide_ua, git_str_cstr(&ua)) < 0) {
|
||||
git_error_set(GIT_ERROR_OS, "unable to convert host to wide characters");
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
@@ -1,17 +1,52 @@
|
||||
#include "clar_libgit2.h"
|
||||
#include "settings.h"
|
||||
|
||||
void test_core_useragent__get(void)
|
||||
static git_buf default_ua = GIT_BUF_INIT;
|
||||
static git_buf default_product = GIT_BUF_INIT;
|
||||
|
||||
void test_core_useragent__initialize(void)
|
||||
{
|
||||
const char *custom_name = "super duper git";
|
||||
git_str buf = GIT_STR_INIT;
|
||||
|
||||
cl_assert_equal_p(NULL, git_settings__user_agent());
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_USER_AGENT, custom_name));
|
||||
cl_assert_equal_s(custom_name, git_settings__user_agent());
|
||||
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_GET_USER_AGENT, &buf));
|
||||
cl_assert_equal_s(custom_name, buf.ptr);
|
||||
|
||||
git_str_dispose(&buf);
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_GET_USER_AGENT, &default_ua));
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_GET_USER_AGENT_PRODUCT, &default_product));
|
||||
}
|
||||
|
||||
void test_core_useragent__cleanup(void)
|
||||
{
|
||||
git_libgit2_opts(GIT_OPT_SET_USER_AGENT, NULL);
|
||||
git_libgit2_opts(GIT_OPT_SET_USER_AGENT_PRODUCT, NULL);
|
||||
|
||||
git_buf_dispose(&default_ua);
|
||||
git_buf_dispose(&default_product);
|
||||
}
|
||||
|
||||
void test_core_useragent__get_default(void)
|
||||
{
|
||||
cl_assert(default_ua.size);
|
||||
cl_assert(default_ua.ptr);
|
||||
cl_assert(git__prefixcmp(default_ua.ptr, "libgit2 ") == 0);
|
||||
|
||||
cl_assert(default_product.size);
|
||||
cl_assert(default_product.ptr);
|
||||
cl_assert(git__prefixcmp(default_product.ptr, "git/") == 0);
|
||||
}
|
||||
|
||||
void test_core_useragent__set(void)
|
||||
{
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_USER_AGENT, "foo bar 4.24"));
|
||||
cl_assert_equal_s("foo bar 4.24", git_settings__user_agent());
|
||||
cl_assert_equal_s(default_product.ptr, git_settings__user_agent_product());
|
||||
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_USER_AGENT_PRODUCT, "baz/2.2.3"));
|
||||
cl_assert_equal_s("foo bar 4.24", git_settings__user_agent());
|
||||
cl_assert_equal_s("baz/2.2.3", git_settings__user_agent_product());
|
||||
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_USER_AGENT, ""));
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_USER_AGENT_PRODUCT, ""));
|
||||
cl_assert_equal_s("", git_settings__user_agent());
|
||||
cl_assert_equal_s("", git_settings__user_agent_product());
|
||||
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_USER_AGENT, NULL));
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_USER_AGENT_PRODUCT, NULL));
|
||||
cl_assert_equal_s(default_ua.ptr, git_settings__user_agent());
|
||||
cl_assert_equal_s(default_product.ptr, git_settings__user_agent_product());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user