Merge pull request #3448 from libgit2/cmn/custom-agent Support setting custom user-agent
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 359e78d..7d5a416 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,9 @@ v0.23 + 1
* Symlinks are now followed when locking a file, which can be
necessary when multiple worktrees share a base repository.
+* You can now set your own user-agent to be sent for HTTP requests by
+ using the `GIT_OPT_SET_USER_AGENT` with `git_libgit2_opts()`.
+
### API additions
* `git_config_lock()` has been added, which allow for
diff --git a/include/git2/common.h b/include/git2/common.h
index 5779061..bf341e7 100644
--- a/include/git2/common.h
+++ b/include/git2/common.h
@@ -145,6 +145,7 @@ typedef enum {
GIT_OPT_GET_TEMPLATE_PATH,
GIT_OPT_SET_TEMPLATE_PATH,
GIT_OPT_SET_SSL_CERT_LOCATIONS,
+ GIT_OPT_SET_USER_AGENT,
} git_libgit2_opt_t;
/**
@@ -240,6 +241,8 @@ typedef enum {
* >
* > Either parameter may be `NULL`, but not both.
*
+ * * opts(GIT_OPT_SET_USER_AGENT, const char *user_agent)
+ *
* @param option Option key
* @param ... value to set the option
* @return 0 on success, <0 on failure
diff --git a/src/global.c b/src/global.c
index 3d37ee4..0eab8d5 100644
--- a/src/global.c
+++ b/src/global.c
@@ -31,6 +31,7 @@ static git_mutex *openssl_locks;
static git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB];
static git_atomic git__n_shutdown_callbacks;
static git_atomic git__n_inits;
+char *git__user_agent;
void git__on_shutdown(git_global_shutdown_fn callback)
{
@@ -269,6 +270,8 @@ int git_libgit2_shutdown(void)
git_win32__crtdbg_stacktrace_cleanup();
git_win32__stack_cleanup();
#endif
+
+ git__free(git__user_agent);
}
/* Exit the lock */
@@ -369,6 +372,7 @@ int git_libgit2_shutdown(void)
git__global_state_cleanup(ptr);
git__free(ptr);
+ git__free(git__user_agent);
pthread_key_delete(_tls_key);
git_mutex_free(&git__mwindow_mutex);
@@ -423,6 +427,7 @@ int git_libgit2_shutdown(void)
git__shutdown();
git__global_state_cleanup(&__state);
uninit_ssl();
+ git__free(git__user_agent);
return 0;
}
diff --git a/src/global.h b/src/global.h
index 37e909a..9fdcee5 100644
--- a/src/global.h
+++ b/src/global.h
@@ -35,4 +35,6 @@ extern void git__on_shutdown(git_global_shutdown_fn callback);
extern void git__free_tls_data(void);
+extern const char *git_libgit2__user_agent(void);
+
#endif
diff --git a/src/settings.c b/src/settings.c
index 2097ca3..030d285 100644
--- a/src/settings.c
+++ b/src/settings.c
@@ -57,6 +57,13 @@ static int config_level_to_sysdir(int config_level)
return val;
}
+extern char *git__user_agent;
+
+const char *git_libgit2__user_agent()
+{
+ return git__user_agent;
+}
+
int git_libgit2_opts(int key, ...)
{
int error = 0;
@@ -153,6 +160,15 @@ 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) {
+ giterr_set_oom();
+ error = -1;
+ }
+
+ break;
}
va_end(ap);
diff --git a/src/transports/http.c b/src/transports/http.c
index e5f2b9f..88b124b 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -10,6 +10,7 @@
#include "http_parser.h"
#include "buffer.h"
#include "netops.h"
+#include "global.h"
#include "remote.h"
#include "smart.h"
#include "auth.h"
@@ -186,6 +187,16 @@ static int apply_credentials(git_buf *buf, http_subtransport *t)
return context->next_token(buf, context, cred);
}
+static const char *user_agent(void)
+{
+ const char *custom = git_libgit2__user_agent();
+
+ if (custom)
+ return custom;
+
+ return "libgit2 " LIBGIT2_VERSION;
+}
+
static int gen_request(
git_buf *buf,
http_stream *s,
@@ -197,7 +208,7 @@ static int gen_request(
git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, path, s->service_url);
- git_buf_puts(buf, "User-Agent: git/1.0 (libgit2 " LIBGIT2_VERSION ")\r\n");
+ git_buf_printf(buf, "User-Agent: git/1.0 (%s)\r\n", user_agent());
git_buf_printf(buf, "Host: %s\r\n", t->connection_data.host);
if (s->chunked || content_length > 0) {
diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c
index b364e90..77d939b 100644
--- a/src/transports/winhttp.c
+++ b/src/transports/winhttp.c
@@ -15,6 +15,7 @@
#include "smart.h"
#include "remote.h"
#include "repository.h"
+#include "global.h"
#include <wincrypt.h>
#include <winhttp.h>
@@ -567,12 +568,28 @@ static int winhttp_close_connection(winhttp_subtransport *t)
return ret;
}
+static int user_agent(git_buf *ua)
+{
+ const char *custom = git_libgit2__user_agent();
+
+ git_buf_clear(ua);
+ git_buf_PUTS(ua, "git/1.0 (");
+
+ if (custom)
+ git_buf_puts(ua, custom);
+ else
+ git_buf_PUTS(ua, "libgit2 " LIBGIT2_VERSION);
+
+ return git_buf_putc(ua, ')');
+}
+
static int winhttp_connect(
winhttp_subtransport *t)
{
- wchar_t *ua = L"git/1.0 (libgit2 " WIDEN(LIBGIT2_VERSION) L")";
wchar_t *wide_host;
int32_t port;
+ wchar_t *wide_ua;
+ git_buf ua = GIT_BUF_INIT;
int error = -1;
int default_timeout = TIMEOUT_INFINITE;
int default_connect_timeout = DEFAULT_CONNECT_TIMEOUT;
@@ -590,9 +607,23 @@ static int winhttp_connect(
return -1;
}
+ if ((error = user_agent(&ua)) < 0) {
+ git__free(wide_host);
+ return error;
+ }
+
+ if (git__utf8_to_16_alloc(&wide_ua, git_buf_cstr(&ua)) < 0) {
+ giterr_set(GITERR_OS, "Unable to convert host to wide characters");
+ git__free(wide_host);
+ git_buf_free(&ua);
+ return -1;
+ }
+
+ git_buf_free(&ua);
+
/* Establish session */
t->session = WinHttpOpen(
- ua,
+ wide_ua,
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS,
@@ -628,6 +659,7 @@ on_error:
winhttp_close_connection(t);
git__free(wide_host);
+ git__free(wide_ua);
return error;
}
diff --git a/tests/core/useragent.c b/tests/core/useragent.c
new file mode 100644
index 0000000..6d06693
--- /dev/null
+++ b/tests/core/useragent.c
@@ -0,0 +1,11 @@
+#include "clar_libgit2.h"
+#include "global.h"
+
+void test_core_useragent__get(void)
+{
+ const char *custom_name = "super duper git";
+
+ cl_assert_equal_p(NULL, git_libgit2__user_agent());
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_USER_AGENT, custom_name));
+ cl_assert_equal_s(custom_name, git_libgit2__user_agent());
+}