summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilip Kelley <phkelley@hotmail.com>2013-03-25 17:35:36 -0400
committerPhilip Kelley <phkelley@hotmail.com>2013-03-25 17:35:36 -0400
commit2c7f7a66e9da70b90332a6c5d484ba8a77e42db4 (patch)
tree369d04a52ffa8c8fa7585e18546640ae95bb5492
parent13640d1bb8376e3f07f66498a5b9bdde9ff3d7d6 (diff)
downloadlibgit2-2c7f7a66e9da70b90332a6c5d484ba8a77e42db4.tar.gz
http: Support 302 Found (arrbee did most of the work)
-rw-r--r--.travis.yml2
-rw-r--r--CMakeLists.txt2
-rw-r--r--src/transports/http.c128
3 files changed, 98 insertions, 34 deletions
diff --git a/.travis.yml b/.travis.yml
index 191ef9441..ad1172dfa 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -33,7 +33,7 @@ script:
# Run Tests
after_success:
- - valgrind --leak-check=full --show-reachable=yes --suppressions=../libgit2_clar.supp ./libgit2_clar
+ - valgrind --leak-check=full --show-reachable=yes --suppressions=../libgit2_clar.supp ./libgit2_clar -ionline
# Only watch the development branch
branches:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 56514cd6e..dfca73630 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -349,7 +349,7 @@ IF (BUILD_CLAR)
ENDIF ()
ENABLE_TESTING()
- ADD_TEST(libgit2_clar libgit2_clar)
+ ADD_TEST(libgit2_clar libgit2_clar -ionline)
ENDIF ()
IF (TAGS)
diff --git a/src/transports/http.c b/src/transports/http.c
index 964bafb19..851d0c0ec 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -45,12 +45,14 @@ typedef struct {
git_smart_subtransport_stream parent;
const char *service;
const char *service_url;
+ char *redirect_url;
const char *verb;
char *chunk_buffer;
unsigned chunk_buffer_len;
unsigned sent_request : 1,
received_response : 1,
- chunked : 1;
+ chunked : 1,
+ redirect_count : 3;
} http_stream;
typedef struct {
@@ -76,6 +78,7 @@ typedef struct {
git_buf parse_header_value;
char parse_buffer_data[2048];
char *content_type;
+ char *location;
git_vector www_authenticate;
enum last_cb last_cb;
int parse_error;
@@ -126,7 +129,12 @@ static int gen_request(
if (!t->path)
t->path = "/";
- git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, t->path, s->service_url);
+ /* If we were redirected, make sure to respect that here */
+ if (s->redirect_url)
+ git_buf_printf(buf, "%s %s HTTP/1.1\r\n", s->verb, s->redirect_url);
+ else
+ git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, t->path, s->service_url);
+
git_buf_puts(buf, "User-Agent: git/1.0 (libgit2 " LIBGIT2_VERSION ")\r\n");
git_buf_printf(buf, "Host: %s\r\n", t->host);
@@ -186,17 +194,25 @@ static int on_header_ready(http_subtransport *t)
{
git_buf *name = &t->parse_header_name;
git_buf *value = &t->parse_header_value;
- char *dup;
- if (!t->content_type && !strcasecmp("Content-Type", git_buf_cstr(name))) {
- t->content_type = git__strdup(git_buf_cstr(value));
- GITERR_CHECK_ALLOC(t->content_type);
+ if (!strcasecmp("Content-Type", git_buf_cstr(name))) {
+ if (!t->content_type) {
+ t->content_type = git__strdup(git_buf_cstr(value));
+ GITERR_CHECK_ALLOC(t->content_type);
+ }
}
else if (!strcmp("WWW-Authenticate", git_buf_cstr(name))) {
- dup = git__strdup(git_buf_cstr(value));
+ char *dup = git__strdup(git_buf_cstr(value));
GITERR_CHECK_ALLOC(dup);
+
git_vector_insert(&t->www_authenticate, dup);
}
+ else if (!strcasecmp("Location", git_buf_cstr(name))) {
+ if (!t->location) {
+ t->location= git__strdup(git_buf_cstr(value));
+ GITERR_CHECK_ALLOC(t->location);
+ }
+ }
return 0;
}
@@ -279,6 +295,35 @@ static int on_headers_complete(http_parser *parser)
}
}
+ /* Check for a 302 Found (redirect).
+ * Right now we only permit a redirect to the same hostname. */
+ if (parser->status_code == 302 &&
+ t->location) {
+
+ if (s->redirect_count >= 7) {
+ giterr_set(GITERR_NET, "Too many redirects");
+ return t->parse_error = PARSE_ERROR_GENERIC;
+ }
+
+ if (t->location[0] != '/') {
+ giterr_set(GITERR_NET, "Only relative redirects are supported");
+ return t->parse_error = PARSE_ERROR_GENERIC;
+ }
+
+ /* Set the redirect URL on the stream. This is a transfer of
+ * ownership of the memory. */
+ if (s->redirect_url)
+ git__free(s->redirect_url);
+
+ s->redirect_url = t->location;
+ t->location = NULL;
+
+ t->connected = 0;
+ s->redirect_count++;
+
+ return t->parse_error = PARSE_ERROR_REPLAY;
+ }
+
/* Check for a 200 HTTP status code. */
if (parser->status_code != 200) {
giterr_set(GITERR_NET,
@@ -371,6 +416,9 @@ static void clear_parser_state(http_subtransport *t)
git__free(t->content_type);
t->content_type = NULL;
+ git__free(t->location);
+ t->location = NULL;
+
git_vector_foreach(&t->www_authenticate, i, entry)
git__free(entry);
@@ -405,6 +453,37 @@ static int write_chunk(gitno_socket *socket, const char *buffer, size_t len)
return 0;
}
+static int http_connect(http_subtransport *t)
+{
+ int flags = 0;
+
+ if (t->connected &&
+ http_should_keep_alive(&t->parser) &&
+ http_body_is_final(&t->parser))
+ return 0;
+
+ if (t->socket.socket)
+ gitno_close(&t->socket);
+
+ if (t->use_ssl) {
+ int tflags;
+
+ if (t->owner->parent.read_flags(&t->owner->parent, &tflags) < 0)
+ return -1;
+
+ flags |= GITNO_CONNECT_SSL;
+
+ if (GIT_TRANSPORTFLAGS_NO_CHECK_CERT & tflags)
+ flags |= GITNO_CONNECT_SSL_NO_CHECK_CERT;
+ }
+
+ if (gitno_connect(&t->socket, t->host, t->port, flags) < 0)
+ return -1;
+
+ t->connected = 1;
+ return 0;
+}
+
static int http_stream_read(
git_smart_subtransport_stream *stream,
char *buffer,
@@ -491,6 +570,10 @@ replay:
* will have signaled us that we should replay the request. */
if (PARSE_ERROR_REPLAY == t->parse_error) {
s->sent_request = 0;
+
+ if (http_connect(t) < 0)
+ return -1;
+
goto replay;
}
@@ -627,6 +710,9 @@ static void http_stream_free(git_smart_subtransport_stream *stream)
if (s->chunk_buffer)
git__free(s->chunk_buffer);
+ if (s->redirect_url)
+ git__free(s->redirect_url);
+
git__free(s);
}
@@ -734,7 +820,7 @@ static int http_action(
{
http_subtransport *t = (http_subtransport *)subtransport;
const char *default_port = NULL;
- int flags = 0, ret;
+ int ret;
if (!stream)
return -1;
@@ -761,30 +847,8 @@ static int http_action(
t->path = strchr(url, '/');
}
- if (!t->connected ||
- !http_should_keep_alive(&t->parser) ||
- !http_body_is_final(&t->parser)) {
-
- if (t->socket.socket)
- gitno_close(&t->socket);
-
- if (t->use_ssl) {
- int transport_flags;
-
- if (t->owner->parent.read_flags(&t->owner->parent, &transport_flags) < 0)
- return -1;
-
- flags |= GITNO_CONNECT_SSL;
-
- if (GIT_TRANSPORTFLAGS_NO_CHECK_CERT & transport_flags)
- flags |= GITNO_CONNECT_SSL_NO_CHECK_CERT;
- }
-
- if (gitno_connect(&t->socket, t->host, t->port, flags) < 0)
- return -1;
-
- t->connected = 1;
- }
+ if (http_connect(t) < 0)
+ return -1;
switch (action)
{