Skip to content

Commit fad9042

Browse files
committed
streams: sockets are non-blocking and can timeout
Make socket I/O non-blocking and add optional timeouts. Users may now set `GIT_OPT_SET_SERVER_CONNECT_TIMEOUT` to set a shorter connection timeout. (The connect timeout cannot be longer than the operating system default.) Users may also now configure the socket read and write timeouts with `GIT_OPT_SET_SERVER_TIMEOUT`. By default, connects still timeout based on the operating system defaults (typically 75 seconds) and socket read and writes block. Add a test against our custom testing git server that ensures that we can timeout reads against a slow server.
1 parent 933b04c commit fad9042

File tree

9 files changed

+335
-30
lines changed

9 files changed

+335
-30
lines changed

ci/test.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,9 +271,13 @@ if [ -z "$SKIP_ONLINE_TESTS" ]; then
271271
272272
export GITTEST_REMOTE_REDIRECT_INITIAL="http://localhost:9000/initial-redirect/libgit2/TestGitRepository"
273273
export GITTEST_REMOTE_REDIRECT_SUBSEQUENT="http://localhost:9000/subsequent-redirect/libgit2/TestGitRepository"
274+
export GITTEST_REMOTE_SPEED_SLOW="http://localhost:9000/speed-9600/test.git"
275+
export GITTEST_REMOTE_SPEED_TIMESOUT="http://localhost:9000/speed-0.5/test.git"
274276
run_test online
275277
unset GITTEST_REMOTE_REDIRECT_INITIAL
276278
unset GITTEST_REMOTE_REDIRECT_SUBSEQUENT
279+
unset GITTEST_REMOTE_SPEED_SLOW
280+
unset GITTEST_REMOTE_SPEED_TIMESOUT
277281
278282
# Run the online tests that immutably change global state separately
279283
# to avoid polluting the test environment.

include/git2/common.h

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,11 @@ typedef enum {
224224
GIT_OPT_GET_OWNER_VALIDATION,
225225
GIT_OPT_SET_OWNER_VALIDATION,
226226
GIT_OPT_GET_HOMEDIR,
227-
GIT_OPT_SET_HOMEDIR
227+
GIT_OPT_SET_HOMEDIR,
228+
GIT_OPT_SET_SERVER_CONNECT_TIMEOUT,
229+
GIT_OPT_GET_SERVER_CONNECT_TIMEOUT,
230+
GIT_OPT_SET_SERVER_TIMEOUT,
231+
GIT_OPT_GET_SERVER_TIMEOUT
228232
} git_libgit2_opt_t;
229233

230234
/**
@@ -480,6 +484,27 @@ typedef enum {
480484
* >
481485
* > - `path` directory of home directory.
482486
*
487+
* opts(GIT_OPT_GET_SERVER_CONNECT_TIMEOUT, int *timeout)
488+
* > Gets the timeout (in milliseconds) to attempt connections to
489+
* > a remote server.
490+
*
491+
* opts(GIT_OPT_SET_SERVER_CONNECT_TIMEOUT, int timeout)
492+
* > Sets the timeout (in milliseconds) to attempt connections to
493+
* > a remote server. This is supported only for HTTP(S) connections
494+
* > and is not supported by SSH. Set to 0 to use the system default.
495+
* > Note that this may not be able to be configured longer than the
496+
* > system default, typically 75 seconds.
497+
*
498+
* opts(GIT_OPT_GET_SERVER_TIMEOUT, int *timeout)
499+
* > Gets the timeout (in milliseconds) for reading from and writing
500+
* > to a remote server.
501+
*
502+
* opts(GIT_OPT_SET_SERVER_TIMEOUT, int timeout)
503+
* > Sets the timeout (in milliseconds) for reading from and writing
504+
* > to a remote server. This is supported only for HTTP(S)
505+
* > connections and is not supported by SSH. Set to 0 to use the
506+
* > system default.
507+
*
483508
* @param option Option key
484509
* @param ... value to set the option
485510
* @return 0 on success, <0 on failure

include/git2/errors.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ typedef enum {
5858
GIT_EMISMATCH = -33, /**< Hashsum mismatch in object */
5959
GIT_EINDEXDIRTY = -34, /**< Unsaved changes in the index would be overwritten */
6060
GIT_EAPPLYFAIL = -35, /**< Patch application failed */
61-
GIT_EOWNER = -36 /**< The object is not owned by the current user */
61+
GIT_EOWNER = -36, /**< The object is not owned by the current user */
62+
GIT_TIMEOUT = -37 /**< The operation timed out */
6263
} git_error_code;
6364

6465
/**

include/git2/sys/stream.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,22 @@ GIT_BEGIN_DECL
2929
typedef struct git_stream {
3030
int version;
3131

32-
int encrypted;
33-
int proxy_support;
32+
int encrypted : 1,
33+
proxy_support : 1;
34+
35+
/**
36+
* Timeout for read and write operations; can be set to `0` to
37+
* block indefinitely.
38+
*/
39+
int timeout;
40+
41+
/**
42+
* Timeout to connect to the remote server; can be set to `0`
43+
* to use the system defaults. This can be shorter than the
44+
* system default - often 75 seconds - but cannot be longer.
45+
*/
46+
int connect_timeout;
47+
3448
int GIT_CALLBACK(connect)(struct git_stream *);
3549
int GIT_CALLBACK(certificate)(git_cert **, struct git_stream *);
3650
int GIT_CALLBACK(set_proxy)(struct git_stream *, const git_proxy_options *proxy_opts);

src/libgit2/libgit2.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ extern size_t git_indexer__max_objects;
4848
extern bool git_disable_pack_keep_file_checks;
4949
extern int git_odb__packed_priority;
5050
extern int git_odb__loose_priority;
51+
extern int git_socket_stream__connect_timeout;
52+
extern int git_socket_stream__timeout;
5153

5254
char *git__user_agent;
5355
char *git__ssl_ciphers;
@@ -436,6 +438,40 @@ int git_libgit2_opts(int key, ...)
436438
error = git_sysdir_set(GIT_SYSDIR_HOME, va_arg(ap, const char *));
437439
break;
438440

441+
case GIT_OPT_GET_SERVER_CONNECT_TIMEOUT:
442+
*(va_arg(ap, int *)) = git_socket_stream__connect_timeout;
443+
break;
444+
445+
case GIT_OPT_SET_SERVER_CONNECT_TIMEOUT:
446+
{
447+
int timeout = va_arg(ap, int);
448+
449+
if (timeout < 0) {
450+
git_error_set(GIT_ERROR_INVALID, "invalid connect timeout");
451+
error = -1;
452+
} else {
453+
git_socket_stream__connect_timeout = timeout;
454+
}
455+
}
456+
break;
457+
458+
case GIT_OPT_GET_SERVER_TIMEOUT:
459+
*(va_arg(ap, int *)) = git_socket_stream__timeout;
460+
break;
461+
462+
case GIT_OPT_SET_SERVER_TIMEOUT:
463+
{
464+
int timeout = va_arg(ap, int);
465+
466+
if (timeout < 0) {
467+
git_error_set(GIT_ERROR_INVALID, "invalid timeout");
468+
error = -1;
469+
} else {
470+
git_socket_stream__timeout = timeout;
471+
}
472+
}
473+
break;
474+
439475
default:
440476
git_error_set(GIT_ERROR_INVALID, "invalid option key");
441477
error = -1;

0 commit comments

Comments
 (0)