|
Packit Service |
20376f |
/*
|
|
Packit Service |
20376f |
* Copyright (C) the libgit2 contributors. All rights reserved.
|
|
Packit Service |
20376f |
*
|
|
Packit Service |
20376f |
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
|
Packit Service |
20376f |
* a Linking Exception. For full terms see the included COPYING file.
|
|
Packit Service |
20376f |
*/
|
|
Packit Service |
20376f |
#ifndef GIT_WINHTTP
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
#include "git2.h"
|
|
Packit Service |
20376f |
#include "http_parser.h"
|
|
Packit Service |
20376f |
#include "buffer.h"
|
|
Packit Service |
20376f |
#include "netops.h"
|
|
Packit Service |
20376f |
#include "global.h"
|
|
Packit Service |
20376f |
#include "remote.h"
|
|
Packit Service |
20376f |
#include "smart.h"
|
|
Packit Service |
20376f |
#include "auth.h"
|
|
Packit Service |
20376f |
#include "auth_negotiate.h"
|
|
Packit Service |
20376f |
#include "tls_stream.h"
|
|
Packit Service |
20376f |
#include "socket_stream.h"
|
|
Packit Service |
20376f |
#include "curl_stream.h"
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_http_auth_scheme auth_schemes[] = {
|
|
Packit Service |
20376f |
{ GIT_AUTHTYPE_NEGOTIATE, "Negotiate", GIT_CREDTYPE_DEFAULT, git_http_auth_negotiate },
|
|
Packit Service |
20376f |
{ GIT_AUTHTYPE_BASIC, "Basic", GIT_CREDTYPE_USERPASS_PLAINTEXT, git_http_auth_basic },
|
|
Packit Service |
20376f |
};
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static const char *upload_pack_service = "upload-pack";
|
|
Packit Service |
20376f |
static const char *upload_pack_ls_service_url = "/info/refs?service=git-upload-pack";
|
|
Packit Service |
20376f |
static const char *upload_pack_service_url = "/git-upload-pack";
|
|
Packit Service |
20376f |
static const char *receive_pack_service = "receive-pack";
|
|
Packit Service |
20376f |
static const char *receive_pack_ls_service_url = "/info/refs?service=git-receive-pack";
|
|
Packit Service |
20376f |
static const char *receive_pack_service_url = "/git-receive-pack";
|
|
Packit Service |
20376f |
static const char *get_verb = "GET";
|
|
Packit Service |
20376f |
static const char *post_verb = "POST";
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
#define OWNING_SUBTRANSPORT(s) ((http_subtransport *)(s)->parent.subtransport)
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
#define PARSE_ERROR_GENERIC -1
|
|
Packit Service |
20376f |
#define PARSE_ERROR_REPLAY -2
|
|
Packit Service |
20376f |
/** Look at the user field */
|
|
Packit Service |
20376f |
#define PARSE_ERROR_EXT -3
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
#define CHUNK_SIZE 4096
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
enum last_cb {
|
|
Packit Service |
20376f |
NONE,
|
|
Packit Service |
20376f |
FIELD,
|
|
Packit Service |
20376f |
VALUE
|
|
Packit Service |
20376f |
};
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
typedef struct {
|
|
Packit Service |
20376f |
git_smart_subtransport_stream parent;
|
|
Packit Service |
20376f |
const char *service;
|
|
Packit Service |
20376f |
const char *service_url;
|
|
Packit Service |
20376f |
char *redirect_url;
|
|
Packit Service |
20376f |
const char *verb;
|
|
Packit Service |
20376f |
char *chunk_buffer;
|
|
Packit Service |
20376f |
unsigned chunk_buffer_len;
|
|
Packit Service |
20376f |
unsigned sent_request : 1,
|
|
Packit Service |
20376f |
received_response : 1,
|
|
Packit Service |
20376f |
chunked : 1,
|
|
Packit Service |
20376f |
redirect_count : 3;
|
|
Packit Service |
20376f |
} http_stream;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
typedef struct {
|
|
Packit Service |
20376f |
git_smart_subtransport parent;
|
|
Packit Service |
20376f |
transport_smart *owner;
|
|
Packit Service |
20376f |
git_stream *io;
|
|
Packit Service |
20376f |
gitno_connection_data connection_data;
|
|
Packit Service |
20376f |
bool connected;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Parser structures */
|
|
Packit Service |
20376f |
http_parser parser;
|
|
Packit Service |
20376f |
http_parser_settings settings;
|
|
Packit Service |
20376f |
gitno_buffer parse_buffer;
|
|
Packit Service |
20376f |
git_buf parse_header_name;
|
|
Packit Service |
20376f |
git_buf parse_header_value;
|
|
Packit Service |
20376f |
char parse_buffer_data[NETIO_BUFSIZE];
|
|
Packit Service |
20376f |
char *content_type;
|
|
Packit Service |
20376f |
char *location;
|
|
Packit Service |
20376f |
git_vector www_authenticate;
|
|
Packit Service |
20376f |
enum last_cb last_cb;
|
|
Packit Service |
20376f |
int parse_error;
|
|
Packit Service |
20376f |
int error;
|
|
Packit Service |
20376f |
unsigned parse_finished : 1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Authentication */
|
|
Packit Service |
20376f |
git_cred *cred;
|
|
Packit Service |
20376f |
git_cred *url_cred;
|
|
Packit Service |
20376f |
git_vector auth_contexts;
|
|
Packit Service |
20376f |
} http_subtransport;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
typedef struct {
|
|
Packit Service |
20376f |
http_stream *s;
|
|
Packit Service |
20376f |
http_subtransport *t;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Target buffer details from read() */
|
|
Packit Service |
20376f |
char *buffer;
|
|
Packit Service |
20376f |
size_t buf_size;
|
|
Packit Service |
20376f |
size_t *bytes_read;
|
|
Packit Service |
20376f |
} parser_context;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static bool credtype_match(git_http_auth_scheme *scheme, void *data)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
unsigned int credtype = *(unsigned int *)data;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return !!(scheme->credtypes & credtype);
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static bool challenge_match(git_http_auth_scheme *scheme, void *data)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
const char *scheme_name = scheme->name;
|
|
Packit Service |
20376f |
const char *challenge = (const char *)data;
|
|
Packit Service |
20376f |
size_t scheme_len;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
scheme_len = strlen(scheme_name);
|
|
Packit Service |
20376f |
return (strncasecmp(challenge, scheme_name, scheme_len) == 0 &&
|
|
Packit Service |
20376f |
(challenge[scheme_len] == '\0' || challenge[scheme_len] == ' '));
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int auth_context_match(
|
|
Packit Service |
20376f |
git_http_auth_context **out,
|
|
Packit Service |
20376f |
http_subtransport *t,
|
|
Packit Service |
20376f |
bool (*scheme_match)(git_http_auth_scheme *scheme, void *data),
|
|
Packit Service |
20376f |
void *data)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
git_http_auth_scheme *scheme = NULL;
|
|
Packit Service |
20376f |
git_http_auth_context *context = NULL, *c;
|
|
Packit Service |
20376f |
size_t i;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
*out = NULL;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
for (i = 0; i < ARRAY_SIZE(auth_schemes); i++) {
|
|
Packit Service |
20376f |
if (scheme_match(&auth_schemes[i], data)) {
|
|
Packit Service |
20376f |
scheme = &auth_schemes[i];
|
|
Packit Service |
20376f |
break;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (!scheme)
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* See if authentication has already started for this scheme */
|
|
Packit Service |
20376f |
git_vector_foreach(&t->auth_contexts, i, c) {
|
|
Packit Service |
20376f |
if (c->type == scheme->type) {
|
|
Packit Service |
20376f |
context = c;
|
|
Packit Service |
20376f |
break;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (!context) {
|
|
Packit Service |
20376f |
if (scheme->init_context(&context, &t->connection_data) < 0)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
else if (!context)
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
else if (git_vector_insert(&t->auth_contexts, context) < 0)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
*out = context;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int apply_credentials(git_buf *buf, http_subtransport *t)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
git_cred *cred = t->cred;
|
|
Packit Service |
20376f |
git_http_auth_context *context;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Apply the credentials given to us in the URL */
|
|
Packit Service |
20376f |
if (!cred && t->connection_data.user && t->connection_data.pass) {
|
|
Packit Service |
20376f |
if (!t->url_cred &&
|
|
Packit Service |
20376f |
git_cred_userpass_plaintext_new(&t->url_cred,
|
|
Packit Service |
20376f |
t->connection_data.user, t->connection_data.pass) < 0)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
cred = t->url_cred;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (!cred)
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Get or create a context for the best scheme for this cred type */
|
|
Packit Service |
20376f |
if (auth_context_match(&context, t, credtype_match, &cred->credtype) < 0)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return context->next_token(buf, context, cred);
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static const char *user_agent(void)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
const char *custom = git_libgit2__user_agent();
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (custom)
|
|
Packit Service |
20376f |
return custom;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return "libgit2 " LIBGIT2_VERSION;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int gen_request(
|
|
Packit Service |
20376f |
git_buf *buf,
|
|
Packit Service |
20376f |
http_stream *s,
|
|
Packit Service |
20376f |
size_t content_length)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
http_subtransport *t = OWNING_SUBTRANSPORT(s);
|
|
Packit Service |
20376f |
const char *path = t->connection_data.path ? t->connection_data.path : "/";
|
|
Packit Service |
20376f |
size_t i;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, path, s->service_url);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_buf_printf(buf, "User-Agent: git/2.0 (%s)\r\n", user_agent());
|
|
Packit Service |
20376f |
git_buf_printf(buf, "Host: %s\r\n", t->connection_data.host);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (s->chunked || content_length > 0) {
|
|
Packit Service |
20376f |
git_buf_printf(buf, "Accept: application/x-git-%s-result\r\n", s->service);
|
|
Packit Service |
20376f |
git_buf_printf(buf, "Content-Type: application/x-git-%s-request\r\n", s->service);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (s->chunked)
|
|
Packit Service |
20376f |
git_buf_puts(buf, "Transfer-Encoding: chunked\r\n");
|
|
Packit Service |
20376f |
else
|
|
Packit Service |
20376f |
git_buf_printf(buf, "Content-Length: %"PRIuZ "\r\n", content_length);
|
|
Packit Service |
20376f |
} else
|
|
Packit Service |
20376f |
git_buf_puts(buf, "Accept: */*\r\n");
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
for (i = 0; i < t->owner->custom_headers.count; i++) {
|
|
Packit Service |
20376f |
if (t->owner->custom_headers.strings[i])
|
|
Packit Service |
20376f |
git_buf_printf(buf, "%s\r\n", t->owner->custom_headers.strings[i]);
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Apply credentials to the request */
|
|
Packit Service |
20376f |
if (apply_credentials(buf, t) < 0)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_buf_puts(buf, "\r\n");
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (git_buf_oom(buf))
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int parse_authenticate_response(
|
|
Packit Service |
20376f |
git_vector *www_authenticate,
|
|
Packit Service |
20376f |
http_subtransport *t,
|
|
Packit Service |
20376f |
int *allowed_types)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
git_http_auth_context *context;
|
|
Packit Service |
20376f |
char *challenge;
|
|
Packit Service |
20376f |
size_t i;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_vector_foreach(www_authenticate, i, challenge) {
|
|
Packit Service |
20376f |
if (auth_context_match(&context, t, challenge_match, challenge) < 0)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
else if (!context)
|
|
Packit Service |
20376f |
continue;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (context->set_challenge &&
|
|
Packit Service |
20376f |
context->set_challenge(context, challenge) < 0)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
*allowed_types |= context->credtypes;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int on_header_ready(http_subtransport *t)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
git_buf *name = &t->parse_header_name;
|
|
Packit Service |
20376f |
git_buf *value = &t->parse_header_value;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (!strcasecmp("Content-Type", git_buf_cstr(name))) {
|
|
Packit Service |
20376f |
if (!t->content_type) {
|
|
Packit Service |
20376f |
t->content_type = git__strdup(git_buf_cstr(value));
|
|
Packit Service |
20376f |
GITERR_CHECK_ALLOC(t->content_type);
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
else if (!strcasecmp("WWW-Authenticate", git_buf_cstr(name))) {
|
|
Packit Service |
20376f |
char *dup = git__strdup(git_buf_cstr(value));
|
|
Packit Service |
20376f |
GITERR_CHECK_ALLOC(dup);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_vector_insert(&t->www_authenticate, dup);
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
else if (!strcasecmp("Location", git_buf_cstr(name))) {
|
|
Packit Service |
20376f |
if (!t->location) {
|
|
Packit Service |
20376f |
t->location = git__strdup(git_buf_cstr(value));
|
|
Packit Service |
20376f |
GITERR_CHECK_ALLOC(t->location);
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int on_header_field(http_parser *parser, const char *str, size_t len)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
parser_context *ctx = (parser_context *) parser->data;
|
|
Packit Service |
20376f |
http_subtransport *t = ctx->t;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Both parse_header_name and parse_header_value are populated
|
|
Packit Service |
20376f |
* and ready for consumption */
|
|
Packit Service |
20376f |
if (VALUE == t->last_cb)
|
|
Packit Service |
20376f |
if (on_header_ready(t) < 0)
|
|
Packit Service |
20376f |
return t->parse_error = PARSE_ERROR_GENERIC;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (NONE == t->last_cb || VALUE == t->last_cb)
|
|
Packit Service |
20376f |
git_buf_clear(&t->parse_header_name);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (git_buf_put(&t->parse_header_name, str, len) < 0)
|
|
Packit Service |
20376f |
return t->parse_error = PARSE_ERROR_GENERIC;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
t->last_cb = FIELD;
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int on_header_value(http_parser *parser, const char *str, size_t len)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
parser_context *ctx = (parser_context *) parser->data;
|
|
Packit Service |
20376f |
http_subtransport *t = ctx->t;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
assert(NONE != t->last_cb);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (FIELD == t->last_cb)
|
|
Packit Service |
20376f |
git_buf_clear(&t->parse_header_value);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (git_buf_put(&t->parse_header_value, str, len) < 0)
|
|
Packit Service |
20376f |
return t->parse_error = PARSE_ERROR_GENERIC;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
t->last_cb = VALUE;
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int on_headers_complete(http_parser *parser)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
parser_context *ctx = (parser_context *) parser->data;
|
|
Packit Service |
20376f |
http_subtransport *t = ctx->t;
|
|
Packit Service |
20376f |
http_stream *s = ctx->s;
|
|
Packit Service |
20376f |
git_buf buf = GIT_BUF_INIT;
|
|
Packit Service |
20376f |
int error = 0, no_callback = 0, allowed_auth_types = 0;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Both parse_header_name and parse_header_value are populated
|
|
Packit Service |
20376f |
* and ready for consumption. */
|
|
Packit Service |
20376f |
if (VALUE == t->last_cb)
|
|
Packit Service |
20376f |
if (on_header_ready(t) < 0)
|
|
Packit Service |
20376f |
return t->parse_error = PARSE_ERROR_GENERIC;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Capture authentication headers which may be a 401 (authentication
|
|
Packit Service |
20376f |
* is not complete) or a 200 (simply informing us that auth *is*
|
|
Packit Service |
20376f |
* complete.)
|
|
Packit Service |
20376f |
*/
|
|
Packit Service |
20376f |
if (parse_authenticate_response(&t->www_authenticate, t,
|
|
Packit Service |
20376f |
&allowed_auth_types) < 0)
|
|
Packit Service |
20376f |
return t->parse_error = PARSE_ERROR_GENERIC;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Check for an authentication failure. */
|
|
Packit Service |
20376f |
if (parser->status_code == 401 && get_verb == s->verb) {
|
|
Packit Service |
20376f |
if (!t->owner->cred_acquire_cb) {
|
|
Packit Service |
20376f |
no_callback = 1;
|
|
Packit Service |
20376f |
} else {
|
|
Packit Service |
20376f |
if (allowed_auth_types) {
|
|
Packit Service |
20376f |
if (t->cred) {
|
|
Packit Service |
20376f |
t->cred->free(t->cred);
|
|
Packit Service |
20376f |
t->cred = NULL;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
error = t->owner->cred_acquire_cb(&t->cred,
|
|
Packit Service |
20376f |
t->owner->url,
|
|
Packit Service |
20376f |
t->connection_data.user,
|
|
Packit Service |
20376f |
allowed_auth_types,
|
|
Packit Service |
20376f |
t->owner->cred_acquire_payload);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (error == GIT_PASSTHROUGH) {
|
|
Packit Service |
20376f |
no_callback = 1;
|
|
Packit Service |
20376f |
} else if (error < 0) {
|
|
Packit Service |
20376f |
t->error = error;
|
|
Packit Service |
20376f |
return t->parse_error = PARSE_ERROR_EXT;
|
|
Packit Service |
20376f |
} else {
|
|
Packit Service |
20376f |
assert(t->cred);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (!(t->cred->credtype & allowed_auth_types)) {
|
|
Packit Service |
20376f |
giterr_set(GITERR_NET, "credentials callback returned an invalid cred type");
|
|
Packit Service |
20376f |
return t->parse_error = PARSE_ERROR_GENERIC;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Successfully acquired a credential. */
|
|
Packit Service |
20376f |
t->parse_error = PARSE_ERROR_REPLAY;
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (no_callback) {
|
|
Packit Service |
20376f |
giterr_set(GITERR_NET, "authentication required but no callback set");
|
|
Packit Service |
20376f |
return t->parse_error = PARSE_ERROR_GENERIC;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Check for a redirect.
|
|
Packit Service |
20376f |
* Right now we only permit a redirect to the same hostname. */
|
|
Packit Service |
20376f |
if ((parser->status_code == 301 ||
|
|
Packit Service |
20376f |
parser->status_code == 302 ||
|
|
Packit Service |
20376f |
(parser->status_code == 303 && get_verb == s->verb) ||
|
|
Packit Service |
20376f |
parser->status_code == 307) &&
|
|
Packit Service |
20376f |
t->location) {
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (s->redirect_count >= 7) {
|
|
Packit Service |
20376f |
giterr_set(GITERR_NET, "too many redirects");
|
|
Packit Service |
20376f |
return t->parse_error = PARSE_ERROR_GENERIC;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (gitno_connection_data_from_url(&t->connection_data, t->location, s->service_url) < 0)
|
|
Packit Service |
20376f |
return t->parse_error = PARSE_ERROR_GENERIC;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Set the redirect URL on the stream. This is a transfer of
|
|
Packit Service |
20376f |
* ownership of the memory. */
|
|
Packit Service |
20376f |
if (s->redirect_url)
|
|
Packit Service |
20376f |
git__free(s->redirect_url);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
s->redirect_url = t->location;
|
|
Packit Service |
20376f |
t->location = NULL;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
t->connected = 0;
|
|
Packit Service |
20376f |
s->redirect_count++;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
t->parse_error = PARSE_ERROR_REPLAY;
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Check for a 200 HTTP status code. */
|
|
Packit Service |
20376f |
if (parser->status_code != 200) {
|
|
Packit Service |
20376f |
giterr_set(GITERR_NET,
|
|
Packit Service |
20376f |
"unexpected HTTP status code: %d",
|
|
Packit Service |
20376f |
parser->status_code);
|
|
Packit Service |
20376f |
return t->parse_error = PARSE_ERROR_GENERIC;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* The response must contain a Content-Type header. */
|
|
Packit Service |
20376f |
if (!t->content_type) {
|
|
Packit Service |
20376f |
giterr_set(GITERR_NET, "no Content-Type header in response");
|
|
Packit Service |
20376f |
return t->parse_error = PARSE_ERROR_GENERIC;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* The Content-Type header must match our expectation. */
|
|
Packit Service |
20376f |
if (get_verb == s->verb)
|
|
Packit Service |
20376f |
git_buf_printf(&buf,
|
|
Packit Service |
20376f |
"application/x-git-%s-advertisement",
|
|
Packit Service |
20376f |
ctx->s->service);
|
|
Packit Service |
20376f |
else
|
|
Packit Service |
20376f |
git_buf_printf(&buf,
|
|
Packit Service |
20376f |
"application/x-git-%s-result",
|
|
Packit Service |
20376f |
ctx->s->service);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (git_buf_oom(&buf))
|
|
Packit Service |
20376f |
return t->parse_error = PARSE_ERROR_GENERIC;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (strcmp(t->content_type, git_buf_cstr(&buf))) {
|
|
Packit Service |
20376f |
git_buf_free(&buf;;
|
|
Packit Service |
20376f |
giterr_set(GITERR_NET,
|
|
Packit Service |
20376f |
"invalid Content-Type: %s",
|
|
Packit Service |
20376f |
t->content_type);
|
|
Packit Service |
20376f |
return t->parse_error = PARSE_ERROR_GENERIC;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_buf_free(&buf;;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int on_message_complete(http_parser *parser)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
parser_context *ctx = (parser_context *) parser->data;
|
|
Packit Service |
20376f |
http_subtransport *t = ctx->t;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
t->parse_finished = 1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int on_body_fill_buffer(http_parser *parser, const char *str, size_t len)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
parser_context *ctx = (parser_context *) parser->data;
|
|
Packit Service |
20376f |
http_subtransport *t = ctx->t;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* If our goal is to replay the request (either an auth failure or
|
|
Packit Service |
20376f |
* a redirect) then don't bother buffering since we're ignoring the
|
|
Packit Service |
20376f |
* content anyway.
|
|
Packit Service |
20376f |
*/
|
|
Packit Service |
20376f |
if (t->parse_error == PARSE_ERROR_REPLAY)
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (ctx->buf_size < len) {
|
|
Packit Service |
20376f |
giterr_set(GITERR_NET, "can't fit data in the buffer");
|
|
Packit Service |
20376f |
return t->parse_error = PARSE_ERROR_GENERIC;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
memcpy(ctx->buffer, str, len);
|
|
Packit Service |
20376f |
*(ctx->bytes_read) += len;
|
|
Packit Service |
20376f |
ctx->buffer += len;
|
|
Packit Service |
20376f |
ctx->buf_size -= len;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static void clear_parser_state(http_subtransport *t)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
http_parser_init(&t->parser, HTTP_RESPONSE);
|
|
Packit Service |
20376f |
gitno_buffer_setup_fromstream(t->io,
|
|
Packit Service |
20376f |
&t->parse_buffer,
|
|
Packit Service |
20376f |
t->parse_buffer_data,
|
|
Packit Service |
20376f |
sizeof(t->parse_buffer_data));
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
t->last_cb = NONE;
|
|
Packit Service |
20376f |
t->parse_error = 0;
|
|
Packit Service |
20376f |
t->parse_finished = 0;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_buf_free(&t->parse_header_name);
|
|
Packit Service |
20376f |
git_buf_init(&t->parse_header_name, 0);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_buf_free(&t->parse_header_value);
|
|
Packit Service |
20376f |
git_buf_init(&t->parse_header_value, 0);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git__free(t->content_type);
|
|
Packit Service |
20376f |
t->content_type = NULL;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git__free(t->location);
|
|
Packit Service |
20376f |
t->location = NULL;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_vector_free_deep(&t->www_authenticate);
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int write_chunk(git_stream *io, const char *buffer, size_t len)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
git_buf buf = GIT_BUF_INIT;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Chunk header */
|
|
Packit Service |
20376f |
git_buf_printf(&buf, "%" PRIxZ "\r\n", len);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (git_buf_oom(&buf))
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (git_stream_write(io, buf.ptr, buf.size, 0) < 0) {
|
|
Packit Service |
20376f |
git_buf_free(&buf;;
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_buf_free(&buf;;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Chunk body */
|
|
Packit Service |
20376f |
if (len > 0 && git_stream_write(io, buffer, len, 0) < 0)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Chunk footer */
|
|
Packit Service |
20376f |
if (git_stream_write(io, "\r\n", 2, 0) < 0)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int apply_proxy_config(http_subtransport *t)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
int error;
|
|
Packit Service |
20376f |
git_proxy_t proxy_type;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (!git_stream_supports_proxy(t->io))
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
proxy_type = t->owner->proxy.type;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (proxy_type == GIT_PROXY_NONE)
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (proxy_type == GIT_PROXY_AUTO) {
|
|
Packit Service |
20376f |
char *url;
|
|
Packit Service |
20376f |
git_proxy_options opts = GIT_PROXY_OPTIONS_INIT;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if ((error = git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &url)) < 0)
|
|
Packit Service |
20376f |
return error;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
opts.credentials = t->owner->proxy.credentials;
|
|
Packit Service |
20376f |
opts.certificate_check = t->owner->proxy.certificate_check;
|
|
Packit Service |
20376f |
opts.payload = t->owner->proxy.payload;
|
|
Packit Service |
20376f |
opts.type = GIT_PROXY_SPECIFIED;
|
|
Packit Service |
20376f |
opts.url = url;
|
|
Packit Service |
20376f |
error = git_stream_set_proxy(t->io, &opts);
|
|
Packit Service |
20376f |
git__free(url);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return error;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return git_stream_set_proxy(t->io, &t->owner->proxy);
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int http_connect(http_subtransport *t)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
int error;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (t->connected &&
|
|
Packit Service |
20376f |
http_should_keep_alive(&t->parser) &&
|
|
Packit Service |
20376f |
t->parse_finished)
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (t->io) {
|
|
Packit Service |
20376f |
git_stream_close(t->io);
|
|
Packit Service |
20376f |
git_stream_free(t->io);
|
|
Packit Service |
20376f |
t->io = NULL;
|
|
Packit Service |
20376f |
t->connected = 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (t->connection_data.use_ssl) {
|
|
Packit Service |
20376f |
error = git_tls_stream_new(&t->io, t->connection_data.host, t->connection_data.port);
|
|
Packit Service |
20376f |
} else {
|
|
Packit Service |
20376f |
#ifdef GIT_CURL
|
|
Packit Service |
20376f |
error = git_curl_stream_new(&t->io, t->connection_data.host, t->connection_data.port);
|
|
Packit Service |
20376f |
#else
|
|
Packit Service |
20376f |
error = git_socket_stream_new(&t->io, t->connection_data.host, t->connection_data.port);
|
|
Packit Service |
20376f |
#endif
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (error < 0)
|
|
Packit Service |
20376f |
return error;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
GITERR_CHECK_VERSION(t->io, GIT_STREAM_VERSION, "git_stream");
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
apply_proxy_config(t);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
error = git_stream_connect(t->io);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if ((!error || error == GIT_ECERTIFICATE) && t->owner->certificate_check_cb != NULL &&
|
|
Packit Service |
20376f |
git_stream_is_encrypted(t->io)) {
|
|
Packit Service |
20376f |
git_cert *cert;
|
|
Packit Service |
20376f |
int is_valid = (error == GIT_OK);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if ((error = git_stream_certificate(&cert, t->io)) < 0)
|
|
Packit Service |
20376f |
return error;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
giterr_clear();
|
|
Packit Service |
20376f |
error = t->owner->certificate_check_cb(cert, is_valid, t->connection_data.host, t->owner->message_cb_payload);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (error < 0) {
|
|
Packit Service |
20376f |
if (!giterr_last())
|
|
Packit Service |
20376f |
giterr_set(GITERR_NET, "user cancelled certificate check");
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return error;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (error < 0)
|
|
Packit Service |
20376f |
return error;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
t->connected = 1;
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int http_stream_read(
|
|
Packit Service |
20376f |
git_smart_subtransport_stream *stream,
|
|
Packit Service |
20376f |
char *buffer,
|
|
Packit Service |
20376f |
size_t buf_size,
|
|
Packit Service |
20376f |
size_t *bytes_read)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
http_stream *s = (http_stream *)stream;
|
|
Packit Service |
20376f |
http_subtransport *t = OWNING_SUBTRANSPORT(s);
|
|
Packit Service |
20376f |
parser_context ctx;
|
|
Packit Service |
20376f |
size_t bytes_parsed;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
replay:
|
|
Packit Service |
20376f |
*bytes_read = 0;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
assert(t->connected);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (!s->sent_request) {
|
|
Packit Service |
20376f |
git_buf request = GIT_BUF_INIT;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
clear_parser_state(t);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (gen_request(&request, s, 0) < 0)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (git_stream_write(t->io, request.ptr, request.size, 0) < 0) {
|
|
Packit Service |
20376f |
git_buf_free(&request);
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_buf_free(&request);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
s->sent_request = 1;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (!s->received_response) {
|
|
Packit Service |
20376f |
if (s->chunked) {
|
|
Packit Service |
20376f |
assert(s->verb == post_verb);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Flush, if necessary */
|
|
Packit Service |
20376f |
if (s->chunk_buffer_len > 0 &&
|
|
Packit Service |
20376f |
write_chunk(t->io, s->chunk_buffer, s->chunk_buffer_len) < 0)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
s->chunk_buffer_len = 0;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Write the final chunk. */
|
|
Packit Service |
20376f |
if (git_stream_write(t->io, "0\r\n\r\n", 5, 0) < 0)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
s->received_response = 1;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
while (!*bytes_read && !t->parse_finished) {
|
|
Packit Service |
20376f |
size_t data_offset;
|
|
Packit Service |
20376f |
int error;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/*
|
|
Packit Service |
20376f |
* Make the parse_buffer think it's as full of data as
|
|
Packit Service |
20376f |
* the buffer, so it won't try to recv more data than
|
|
Packit Service |
20376f |
* we can put into it.
|
|
Packit Service |
20376f |
*
|
|
Packit Service |
20376f |
* data_offset is the actual data offset from which we
|
|
Packit Service |
20376f |
* should tell the parser to start reading.
|
|
Packit Service |
20376f |
*/
|
|
Packit Service |
20376f |
if (buf_size >= t->parse_buffer.len) {
|
|
Packit Service |
20376f |
t->parse_buffer.offset = 0;
|
|
Packit Service |
20376f |
} else {
|
|
Packit Service |
20376f |
t->parse_buffer.offset = t->parse_buffer.len - buf_size;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
data_offset = t->parse_buffer.offset;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (gitno_recv(&t->parse_buffer) < 0)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* This call to http_parser_execute will result in invocations of the
|
|
Packit Service |
20376f |
* on_* family of callbacks. The most interesting of these is
|
|
Packit Service |
20376f |
* on_body_fill_buffer, which is called when data is ready to be copied
|
|
Packit Service |
20376f |
* into the target buffer. We need to marshal the buffer, buf_size, and
|
|
Packit Service |
20376f |
* bytes_read parameters to this callback. */
|
|
Packit Service |
20376f |
ctx.t = t;
|
|
Packit Service |
20376f |
ctx.s = s;
|
|
Packit Service |
20376f |
ctx.buffer = buffer;
|
|
Packit Service |
20376f |
ctx.buf_size = buf_size;
|
|
Packit Service |
20376f |
ctx.bytes_read = bytes_read;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Set the context, call the parser, then unset the context. */
|
|
Packit Service |
20376f |
t->parser.data = &ctx;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
bytes_parsed = http_parser_execute(&t->parser,
|
|
Packit Service |
20376f |
&t->settings,
|
|
Packit Service |
20376f |
t->parse_buffer.data + data_offset,
|
|
Packit Service |
20376f |
t->parse_buffer.offset - data_offset);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
t->parser.data = NULL;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* If there was a handled authentication failure, then parse_error
|
|
Packit Service |
20376f |
* will have signaled us that we should replay the request. */
|
|
Packit Service |
20376f |
if (PARSE_ERROR_REPLAY == t->parse_error) {
|
|
Packit Service |
20376f |
s->sent_request = 0;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if ((error = http_connect(t)) < 0)
|
|
Packit Service |
20376f |
return error;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
goto replay;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (t->parse_error == PARSE_ERROR_EXT) {
|
|
Packit Service |
20376f |
return t->error;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (t->parse_error < 0)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (bytes_parsed != t->parse_buffer.offset - data_offset) {
|
|
Packit Service |
20376f |
giterr_set(GITERR_NET,
|
|
Packit Service |
20376f |
"HTTP parser error: %s",
|
|
Packit Service |
20376f |
http_errno_description((enum http_errno)t->parser.http_errno));
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int http_stream_write_chunked(
|
|
Packit Service |
20376f |
git_smart_subtransport_stream *stream,
|
|
Packit Service |
20376f |
const char *buffer,
|
|
Packit Service |
20376f |
size_t len)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
http_stream *s = (http_stream *)stream;
|
|
Packit Service |
20376f |
http_subtransport *t = OWNING_SUBTRANSPORT(s);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
assert(t->connected);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Send the request, if necessary */
|
|
Packit Service |
20376f |
if (!s->sent_request) {
|
|
Packit Service |
20376f |
git_buf request = GIT_BUF_INIT;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
clear_parser_state(t);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (gen_request(&request, s, 0) < 0)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (git_stream_write(t->io, request.ptr, request.size, 0) < 0) {
|
|
Packit Service |
20376f |
git_buf_free(&request);
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_buf_free(&request);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
s->sent_request = 1;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (len > CHUNK_SIZE) {
|
|
Packit Service |
20376f |
/* Flush, if necessary */
|
|
Packit Service |
20376f |
if (s->chunk_buffer_len > 0) {
|
|
Packit Service |
20376f |
if (write_chunk(t->io, s->chunk_buffer, s->chunk_buffer_len) < 0)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
s->chunk_buffer_len = 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Write chunk directly */
|
|
Packit Service |
20376f |
if (write_chunk(t->io, buffer, len) < 0)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
else {
|
|
Packit Service |
20376f |
/* Append as much to the buffer as we can */
|
|
Packit Service |
20376f |
int count = min(CHUNK_SIZE - s->chunk_buffer_len, len);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (!s->chunk_buffer)
|
|
Packit Service |
20376f |
s->chunk_buffer = git__malloc(CHUNK_SIZE);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
memcpy(s->chunk_buffer + s->chunk_buffer_len, buffer, count);
|
|
Packit Service |
20376f |
s->chunk_buffer_len += count;
|
|
Packit Service |
20376f |
buffer += count;
|
|
Packit Service |
20376f |
len -= count;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Is the buffer full? If so, then flush */
|
|
Packit Service |
20376f |
if (CHUNK_SIZE == s->chunk_buffer_len) {
|
|
Packit Service |
20376f |
if (write_chunk(t->io, s->chunk_buffer, s->chunk_buffer_len) < 0)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
s->chunk_buffer_len = 0;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (len > 0) {
|
|
Packit Service |
20376f |
memcpy(s->chunk_buffer, buffer, len);
|
|
Packit Service |
20376f |
s->chunk_buffer_len = len;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int http_stream_write_single(
|
|
Packit Service |
20376f |
git_smart_subtransport_stream *stream,
|
|
Packit Service |
20376f |
const char *buffer,
|
|
Packit Service |
20376f |
size_t len)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
http_stream *s = (http_stream *)stream;
|
|
Packit Service |
20376f |
http_subtransport *t = OWNING_SUBTRANSPORT(s);
|
|
Packit Service |
20376f |
git_buf request = GIT_BUF_INIT;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
assert(t->connected);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (s->sent_request) {
|
|
Packit Service |
20376f |
giterr_set(GITERR_NET, "subtransport configured for only one write");
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
clear_parser_state(t);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (gen_request(&request, s, len) < 0)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (git_stream_write(t->io, request.ptr, request.size, 0) < 0)
|
|
Packit Service |
20376f |
goto on_error;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (len && git_stream_write(t->io, buffer, len, 0) < 0)
|
|
Packit Service |
20376f |
goto on_error;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_buf_free(&request);
|
|
Packit Service |
20376f |
s->sent_request = 1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
on_error:
|
|
Packit Service |
20376f |
git_buf_free(&request);
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static void http_stream_free(git_smart_subtransport_stream *stream)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
http_stream *s = (http_stream *)stream;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (s->chunk_buffer)
|
|
Packit Service |
20376f |
git__free(s->chunk_buffer);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (s->redirect_url)
|
|
Packit Service |
20376f |
git__free(s->redirect_url);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git__free(s);
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int http_stream_alloc(http_subtransport *t,
|
|
Packit Service |
20376f |
git_smart_subtransport_stream **stream)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
http_stream *s;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (!stream)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
s = git__calloc(sizeof(http_stream), 1);
|
|
Packit Service |
20376f |
GITERR_CHECK_ALLOC(s);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
s->parent.subtransport = &t->parent;
|
|
Packit Service |
20376f |
s->parent.read = http_stream_read;
|
|
Packit Service |
20376f |
s->parent.write = http_stream_write_single;
|
|
Packit Service |
20376f |
s->parent.free = http_stream_free;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
*stream = (git_smart_subtransport_stream *)s;
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int http_uploadpack_ls(
|
|
Packit Service |
20376f |
http_subtransport *t,
|
|
Packit Service |
20376f |
git_smart_subtransport_stream **stream)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
http_stream *s;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (http_stream_alloc(t, stream) < 0)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
s = (http_stream *)*stream;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
s->service = upload_pack_service;
|
|
Packit Service |
20376f |
s->service_url = upload_pack_ls_service_url;
|
|
Packit Service |
20376f |
s->verb = get_verb;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int http_uploadpack(
|
|
Packit Service |
20376f |
http_subtransport *t,
|
|
Packit Service |
20376f |
git_smart_subtransport_stream **stream)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
http_stream *s;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (http_stream_alloc(t, stream) < 0)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
s = (http_stream *)*stream;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
s->service = upload_pack_service;
|
|
Packit Service |
20376f |
s->service_url = upload_pack_service_url;
|
|
Packit Service |
20376f |
s->verb = post_verb;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int http_receivepack_ls(
|
|
Packit Service |
20376f |
http_subtransport *t,
|
|
Packit Service |
20376f |
git_smart_subtransport_stream **stream)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
http_stream *s;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (http_stream_alloc(t, stream) < 0)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
s = (http_stream *)*stream;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
s->service = receive_pack_service;
|
|
Packit Service |
20376f |
s->service_url = receive_pack_ls_service_url;
|
|
Packit Service |
20376f |
s->verb = get_verb;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int http_receivepack(
|
|
Packit Service |
20376f |
http_subtransport *t,
|
|
Packit Service |
20376f |
git_smart_subtransport_stream **stream)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
http_stream *s;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (http_stream_alloc(t, stream) < 0)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
s = (http_stream *)*stream;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Use Transfer-Encoding: chunked for this request */
|
|
Packit Service |
20376f |
s->chunked = 1;
|
|
Packit Service |
20376f |
s->parent.write = http_stream_write_chunked;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
s->service = receive_pack_service;
|
|
Packit Service |
20376f |
s->service_url = receive_pack_service_url;
|
|
Packit Service |
20376f |
s->verb = post_verb;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int http_action(
|
|
Packit Service |
20376f |
git_smart_subtransport_stream **stream,
|
|
Packit Service |
20376f |
git_smart_subtransport *subtransport,
|
|
Packit Service |
20376f |
const char *url,
|
|
Packit Service |
20376f |
git_smart_service_t action)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
http_subtransport *t = (http_subtransport *)subtransport;
|
|
Packit Service |
20376f |
int ret;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (!stream)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if ((!t->connection_data.host || !t->connection_data.port || !t->connection_data.path) &&
|
|
Packit Service |
20376f |
(ret = gitno_connection_data_from_url(&t->connection_data, url, NULL)) < 0)
|
|
Packit Service |
20376f |
return ret;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if ((ret = http_connect(t)) < 0)
|
|
Packit Service |
20376f |
return ret;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
switch (action) {
|
|
Packit Service |
20376f |
case GIT_SERVICE_UPLOADPACK_LS:
|
|
Packit Service |
20376f |
return http_uploadpack_ls(t, stream);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
case GIT_SERVICE_UPLOADPACK:
|
|
Packit Service |
20376f |
return http_uploadpack(t, stream);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
case GIT_SERVICE_RECEIVEPACK_LS:
|
|
Packit Service |
20376f |
return http_receivepack_ls(t, stream);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
case GIT_SERVICE_RECEIVEPACK:
|
|
Packit Service |
20376f |
return http_receivepack(t, stream);
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
*stream = NULL;
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int http_close(git_smart_subtransport *subtransport)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
http_subtransport *t = (http_subtransport *) subtransport;
|
|
Packit Service |
20376f |
git_http_auth_context *context;
|
|
Packit Service |
20376f |
size_t i;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
clear_parser_state(t);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
t->connected = 0;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (t->io) {
|
|
Packit Service |
20376f |
git_stream_close(t->io);
|
|
Packit Service |
20376f |
git_stream_free(t->io);
|
|
Packit Service |
20376f |
t->io = NULL;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (t->cred) {
|
|
Packit Service |
20376f |
t->cred->free(t->cred);
|
|
Packit Service |
20376f |
t->cred = NULL;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (t->url_cred) {
|
|
Packit Service |
20376f |
t->url_cred->free(t->url_cred);
|
|
Packit Service |
20376f |
t->url_cred = NULL;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_vector_foreach(&t->auth_contexts, i, context) {
|
|
Packit Service |
20376f |
if (context->free)
|
|
Packit Service |
20376f |
context->free(context);
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_vector_clear(&t->auth_contexts);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
gitno_connection_data_free_ptrs(&t->connection_data);
|
|
Packit Service |
20376f |
memset(&t->connection_data, 0x0, sizeof(gitno_connection_data));
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static void http_free(git_smart_subtransport *subtransport)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
http_subtransport *t = (http_subtransport *) subtransport;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
http_close(subtransport);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_vector_free(&t->auth_contexts);
|
|
Packit Service |
20376f |
git__free(t);
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
int git_smart_subtransport_http(git_smart_subtransport **out, git_transport *owner, void *param)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
http_subtransport *t;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
GIT_UNUSED(param);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (!out)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
t = git__calloc(sizeof(http_subtransport), 1);
|
|
Packit Service |
20376f |
GITERR_CHECK_ALLOC(t);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
t->owner = (transport_smart *)owner;
|
|
Packit Service |
20376f |
t->parent.action = http_action;
|
|
Packit Service |
20376f |
t->parent.close = http_close;
|
|
Packit Service |
20376f |
t->parent.free = http_free;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
t->settings.on_header_field = on_header_field;
|
|
Packit Service |
20376f |
t->settings.on_header_value = on_header_value;
|
|
Packit Service |
20376f |
t->settings.on_headers_complete = on_headers_complete;
|
|
Packit Service |
20376f |
t->settings.on_body = on_body_fill_buffer;
|
|
Packit Service |
20376f |
t->settings.on_message_complete = on_message_complete;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
*out = (git_smart_subtransport *) t;
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
#endif /* !GIT_WINHTTP */
|