http.c 35.4 KB
Newer Older
1
/*
Edward Thomson committed
2
 * Copyright (C) the libgit2 contributors. All rights reserved.
3
 *
4 5
 * This file is part of libgit2, distributed under the GNU GPL v2 with
 * a Linking Exception. For full terms see the included COPYING file.
6
 */
7 8 9

#include "common.h"

10 11
#ifndef GIT_WINHTTP

12 13 14
#include "git2.h"
#include "http_parser.h"
#include "buffer.h"
15
#include "netops.h"
16
#include "global.h"
17
#include "remote.h"
18
#include "smart.h"
19
#include "auth.h"
20
#include "http.h"
21
#include "auth_negotiate.h"
22 23
#include "streams/tls.h"
#include "streams/socket.h"
24

25 26 27 28
git_http_auth_scheme auth_schemes[] = {
	{ GIT_AUTHTYPE_NEGOTIATE, "Negotiate", GIT_CREDTYPE_DEFAULT, git_http_auth_negotiate },
	{ GIT_AUTHTYPE_BASIC, "Basic", GIT_CREDTYPE_USERPASS_PLAINTEXT, git_http_auth_basic },
};
29

30 31 32
static const char *upload_pack_service = "upload-pack";
static const char *upload_pack_ls_service_url = "/info/refs?service=git-upload-pack";
static const char *upload_pack_service_url = "/git-upload-pack";
33 34 35
static const char *receive_pack_service = "receive-pack";
static const char *receive_pack_ls_service_url = "/info/refs?service=git-receive-pack";
static const char *receive_pack_service_url = "/git-receive-pack";
36 37 38
static const char *get_verb = "GET";
static const char *post_verb = "POST";

39 40 41
#define AUTH_HEADER_SERVER "Authorization"
#define AUTH_HEADER_PROXY  "Proxy-Authorization"

42 43 44
#define SERVER_TYPE_REMOTE "remote"
#define SERVER_TYPE_PROXY  "proxy"

45
#define OWNING_SUBTRANSPORT(s) ((http_subtransport *)(s)->parent.subtransport)
46

47 48
#define PARSE_ERROR_GENERIC	-1
#define PARSE_ERROR_REPLAY	-2
49 50
/** Look at the user field */
#define PARSE_ERROR_EXT         -3
51

52 53
#define CHUNK_SIZE	4096

54
enum last_cb {
55 56 57
	NONE,
	FIELD,
	VALUE
58
};
59

60
typedef struct {
61 62 63
	git_smart_subtransport_stream parent;
	const char *service;
	const char *service_url;
64
	char *redirect_url;
65
	const char *verb;
66 67 68 69
	char *chunk_buffer;
	unsigned chunk_buffer_len;
	unsigned sent_request : 1,
		received_response : 1,
70
		chunked : 1;
71 72 73
} http_stream;

typedef struct {
74 75 76 77 78 79 80 81 82 83 84
	gitno_connection_data url;
	git_stream *stream;

	git_cred *cred;
	git_cred *url_cred;

	git_vector auth_challenges;
	git_vector auth_contexts;
} http_server;

typedef struct {
85
	git_smart_subtransport parent;
86
	transport_smart *owner;
87
	git_stream *gitserver_stream;
88
	bool connected;
89

90 91 92
	http_server server;

	http_server proxy;
93
	char *proxy_url;
94
	git_proxy_options proxy_opts;
95

96
	/* Parser structures */
97
	http_parser parser;
98 99
	http_parser_settings settings;
	gitno_buffer parse_buffer;
100 101
	git_buf parse_header_name;
	git_buf parse_header_value;
102
	char parse_buffer_data[NETIO_BUFSIZE];
103
	char *content_type;
104
	char *content_length;
105
	char *location;
106
	enum last_cb last_cb;
107
	int parse_error;
108
	int error;
109 110
	unsigned parse_finished : 1,
	    replay_count : 3;
111 112 113 114 115 116 117 118 119 120 121
} http_subtransport;

typedef struct {
	http_stream *s;
	http_subtransport *t;

	/* Target buffer details from read() */
	char *buffer;
	size_t buf_size;
	size_t *bytes_read;
} parser_context;
122

123
static bool credtype_match(git_http_auth_scheme *scheme, void *data)
124
{
125
	unsigned int credtype = *(unsigned int *)data;
126

127
	return !!(scheme->credtypes & credtype);
128 129
}

130
static bool challenge_match(git_http_auth_scheme *scheme, void *data)
131
{
132 133 134 135 136
	const char *scheme_name = scheme->name;
	const char *challenge = (const char *)data;
	size_t scheme_len;

	scheme_len = strlen(scheme_name);
137
	return (strncasecmp(challenge, scheme_name, scheme_len) == 0 &&
138
		(challenge[scheme_len] == '\0' || challenge[scheme_len] == ' '));
139 140
}

141 142
static int auth_context_match(
	git_http_auth_context **out,
143
	http_server *server,
144 145
	bool (*scheme_match)(git_http_auth_scheme *scheme, void *data),
	void *data)
146
{
147 148
	git_http_auth_scheme *scheme = NULL;
	git_http_auth_context *context = NULL, *c;
149 150
	size_t i;

151
	*out = NULL;
152

153 154 155 156
	for (i = 0; i < ARRAY_SIZE(auth_schemes); i++) {
		if (scheme_match(&auth_schemes[i], data)) {
			scheme = &auth_schemes[i];
			break;
157 158 159
		}
	}

160
	if (!scheme)
161
		return 0;
162

163
	/* See if authentication has already started for this scheme */
164
	git_vector_foreach(&server->auth_contexts, i, c) {
165 166 167 168
		if (c->type == scheme->type) {
			context = c;
			break;
		}
169 170
	}

171
	if (!context) {
172
		if (scheme->init_context(&context, &server->url) < 0)
173 174 175
			return -1;
		else if (!context)
			return 0;
176
		else if (git_vector_insert(&server->auth_contexts, context) < 0)
177 178
			return -1;
	}
179

180
	*out = context;
181 182 183 184

	return 0;
}

185 186 187 188
static int apply_credentials(
	git_buf *buf,
	http_server *server,
	const char *header_name)
189
{
190
	git_cred *cred = server->cred;
191 192 193
	git_http_auth_context *context;

	/* Apply the credentials given to us in the URL */
194 195 196 197
	if (!cred && server->url.user && server->url.pass) {
		if (!server->url_cred &&
			git_cred_userpass_plaintext_new(&server->url_cred,
			    server->url.user, server->url.pass) < 0)
198
			return -1;
199

200
		cred = server->url_cred;
201 202
	}

203 204
	if (!cred)
		return 0;
205

206
	/* Get or create a context for the best scheme for this cred type */
207 208
	if (auth_context_match(&context, server,
	    credtype_match, &cred->credtype) < 0)
209 210
		return -1;

211 212 213
	if (!context)
		return 0;

214
	return context->next_token(buf, context, header_name, cred);
215 216
}

217 218
static int gen_request(
	git_buf *buf,
219 220
	http_stream *s,
	size_t content_length)
221
{
222
	http_subtransport *t = OWNING_SUBTRANSPORT(s);
223
	const char *path = t->server.url.path ? t->server.url.path : "/";
224
	size_t i;
225

226
	if (t->proxy_opts.type == GIT_PROXY_SPECIFIED)
227 228
		git_buf_printf(buf, "%s %s://%s:%s%s%s HTTP/1.1\r\n",
			s->verb,
229 230 231
			t->server.url.use_ssl ? "https" : "http",
			t->server.url.host,
			t->server.url.port,
232 233 234 235
			path, s->service_url);
	else
		git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n",
			s->verb, path, s->service_url);
236

237 238 239
	git_buf_puts(buf, "User-Agent: ");
	git_http__user_agent(buf);
	git_buf_puts(buf, "\r\n");
240 241 242
	git_buf_printf(buf, "Host: %s", t->server.url.host);
	if (strcmp(t->server.url.port, gitno__default_port(&t->server.url)) != 0) {
		git_buf_printf(buf, ":%s", t->server.url.port);
243 244
	}
	git_buf_puts(buf, "\r\n");
245 246 247 248 249 250 251 252 253 254

	if (s->chunked || content_length > 0) {
		git_buf_printf(buf, "Accept: application/x-git-%s-result\r\n", s->service);
		git_buf_printf(buf, "Content-Type: application/x-git-%s-request\r\n", s->service);

		if (s->chunked)
			git_buf_puts(buf, "Transfer-Encoding: chunked\r\n");
		else
			git_buf_printf(buf, "Content-Length: %"PRIuZ "\r\n", content_length);
	} else
255
		git_buf_puts(buf, "Accept: */*\r\n");
256

257 258 259
	for (i = 0; i < t->owner->custom_headers.count; i++) {
		if (t->owner->custom_headers.strings[i])
			git_buf_printf(buf, "%s\r\n", t->owner->custom_headers.strings[i]);
260 261
	}

262
	/* Apply proxy and server credentials to the request */
263 264 265 266 267
	if (t->proxy_opts.type != GIT_PROXY_NONE &&
	    apply_credentials(buf, &t->proxy, AUTH_HEADER_PROXY) < 0)
		return -1;

	if (apply_credentials(buf, &t->server, AUTH_HEADER_SERVER) < 0)
268
		return -1;
269

270
	git_buf_puts(buf, "\r\n");
271

272
	if (git_buf_oom(buf))
273
		return -1;
274 275

	return 0;
276 277
}

278
static int parse_authenticate_response(
279
	http_server *server,
280
	int *allowed_types)
281
{
282 283 284
	git_http_auth_context *context;
	char *challenge;
	size_t i;
285

286 287 288
	git_vector_foreach(&server->auth_challenges, i, challenge) {
		if (auth_context_match(&context, server,
		    challenge_match, challenge) < 0)
289 290 291
			return -1;
		else if (!context)
			continue;
292

293 294 295
		if (context->set_challenge &&
			context->set_challenge(context, challenge) < 0)
			return -1;
296

297
		*allowed_types |= context->credtypes;
298 299 300 301 302 303 304 305 306 307
	}

	return 0;
}

static int on_header_ready(http_subtransport *t)
{
	git_buf *name = &t->parse_header_name;
	git_buf *value = &t->parse_header_value;

308
	if (!strcasecmp("Content-Type", git_buf_cstr(name))) {
309
		if (t->content_type) {
310
			git_error_set(GIT_ERROR_NET, "multiple Content-Type headers");
311
			return -1;
312
		}
313 314

		t->content_type = git__strdup(git_buf_cstr(value));
315
		GIT_ERROR_CHECK_ALLOC(t->content_type);
316
	}
317
	else if (!strcasecmp("Content-Length", git_buf_cstr(name))) {
318
		if (t->content_length) {
319
			git_error_set(GIT_ERROR_NET, "multiple Content-Length headers");
320
			return -1;
321
		}
322 323

		t->content_length = git__strdup(git_buf_cstr(value));
324
		GIT_ERROR_CHECK_ALLOC(t->content_length);
325
	}
326 327
	else if (!strcasecmp("Proxy-Authenticate", git_buf_cstr(name))) {
		char *dup = git__strdup(git_buf_cstr(value));
328
		GIT_ERROR_CHECK_ALLOC(dup);
329

330 331
		if (git_vector_insert(&t->proxy.auth_challenges, dup) < 0)
			return -1;
332
	}
333
	else if (!strcasecmp("WWW-Authenticate", git_buf_cstr(name))) {
334
		char *dup = git__strdup(git_buf_cstr(value));
335
		GIT_ERROR_CHECK_ALLOC(dup);
336

337 338
		if (git_vector_insert(&t->server.auth_challenges, dup) < 0)
			return -1;
339
	}
340
	else if (!strcasecmp("Location", git_buf_cstr(name))) {
341
		if (t->location) {
342
			git_error_set(GIT_ERROR_NET, "multiple Location headers");
343
			return -1;
344
		}
345 346

		t->location = git__strdup(git_buf_cstr(value));
347
		GIT_ERROR_CHECK_ALLOC(t->location);
348
	}
349 350 351 352

	return 0;
}

353 354
static int on_header_field(http_parser *parser, const char *str, size_t len)
{
355 356
	parser_context *ctx = (parser_context *) parser->data;
	http_subtransport *t = ctx->t;
357

358 359 360 361 362
	/* Both parse_header_name and parse_header_value are populated
	 * and ready for consumption */
	if (VALUE == t->last_cb)
		if (on_header_ready(t) < 0)
			return t->parse_error = PARSE_ERROR_GENERIC;
363

364 365
	if (NONE == t->last_cb || VALUE == t->last_cb)
		git_buf_clear(&t->parse_header_name);
366

367 368
	if (git_buf_put(&t->parse_header_name, str, len) < 0)
		return t->parse_error = PARSE_ERROR_GENERIC;
369

370
	t->last_cb = FIELD;
371
	return 0;
372 373 374 375
}

static int on_header_value(http_parser *parser, const char *str, size_t len)
{
376 377
	parser_context *ctx = (parser_context *) parser->data;
	http_subtransport *t = ctx->t;
378

379
	assert(NONE != t->last_cb);
380

381 382
	if (FIELD == t->last_cb)
		git_buf_clear(&t->parse_header_value);
383

384 385
	if (git_buf_put(&t->parse_header_value, str, len) < 0)
		return t->parse_error = PARSE_ERROR_GENERIC;
386

387
	t->last_cb = VALUE;
388
	return 0;
389 390
}

391 392 393 394 395 396 397 398
GIT_INLINE(void) free_cred(git_cred **cred)
{
	if (*cred) {
		git_cred_free(*cred);
		(*cred) = NULL;
	}
}

399 400 401 402
static int on_auth_required(
	git_cred **creds,
	http_parser *parser,
	const char *url,
403
	const char *type,
404 405 406 407
	git_cred_acquire_cb callback,
	void *callback_payload,
	const char *username,
	int allowed_types)
408 409 410 411 412 413
{
	parser_context *ctx = (parser_context *) parser->data;
	http_subtransport *t = ctx->t;
	int ret;

	if (!allowed_types) {
414
		git_error_set(GIT_ERROR_NET, "%s requested authentication but did not negotiate mechanisms", type);
415 416 417 418
		t->parse_error = PARSE_ERROR_GENERIC;
		return t->parse_error;
	}

419
	if (callback) {
420
		free_cred(creds);
421
		ret = callback(creds, url, username, allowed_types, callback_payload);
422 423 424 425 426 427 428 429

		if (ret == GIT_PASSTHROUGH) {
			/* treat GIT_PASSTHROUGH as if callback isn't set */
		} else if (ret < 0) {
			t->error = ret;
			t->parse_error = PARSE_ERROR_EXT;
			return t->parse_error;
		} else {
430
			assert(*creds);
431

432
			if (!((*creds)->credtype & allowed_types)) {
433
				git_error_set(GIT_ERROR_NET, "%s credential provider returned an invalid cred type", type);
434 435 436 437 438 439 440 441 442 443
				t->parse_error = PARSE_ERROR_GENERIC;
				return t->parse_error;
			}

			/* Successfully acquired a credential. */
			t->parse_error = PARSE_ERROR_REPLAY;
			return 0;
		}
	}

444
	git_error_set(GIT_ERROR_NET, "%s authentication required but no callback set",
445
		type);
446 447 448 449
	t->parse_error = PARSE_ERROR_GENERIC;
	return t->parse_error;
}

450 451
static int on_headers_complete(http_parser *parser)
{
452 453
	parser_context *ctx = (parser_context *) parser->data;
	http_subtransport *t = ctx->t;
454 455
	http_stream *s = ctx->s;
	git_buf buf = GIT_BUF_INIT;
456
	int proxy_auth_types = 0, server_auth_types = 0;
457

458
	/* Enforce a reasonable cap on the number of replays */
459
	if (t->replay_count++ >= GIT_HTTP_REPLAY_MAX) {
460
		git_error_set(GIT_ERROR_NET, "too many redirects or authentication replays");
461 462 463
		return t->parse_error = PARSE_ERROR_GENERIC;
	}

464 465 466 467 468 469
	/* Both parse_header_name and parse_header_value are populated
	 * and ready for consumption. */
	if (VALUE == t->last_cb)
		if (on_header_ready(t) < 0)
			return t->parse_error = PARSE_ERROR_GENERIC;

470 471 472 473
	/*
	 * Capture authentication headers for the proxy or final endpoint,
	 * these may be 407/401 (authentication is not complete) or a 200
	 * (informing us that auth has completed).
474
	 */
475 476
	if (parse_authenticate_response(&t->proxy, &proxy_auth_types) < 0 ||
	    parse_authenticate_response(&t->server, &server_auth_types) < 0)
477 478
		return t->parse_error = PARSE_ERROR_GENERIC;

479 480
	/* Check for a proxy authentication failure. */
	if (parser->status_code == 407 && get_verb == s->verb)
481
		return on_auth_required(&t->proxy.cred,
482
		    parser,
483
		    t->proxy_opts.url,
484
		    SERVER_TYPE_PROXY,
485 486 487 488
		    t->proxy_opts.credentials,
		    t->proxy_opts.payload,
		    t->proxy.url.user,
			proxy_auth_types);
489

490
	/* Check for an authentication failure. */
491
	if (parser->status_code == 401 && get_verb == s->verb)
492
		return on_auth_required(&t->server.cred,
493 494
		    parser,
		    t->owner->url,
495
		    SERVER_TYPE_REMOTE,
496 497
		    t->owner->cred_acquire_cb,
		    t->owner->cred_acquire_payload,
498 499
		    t->server.url.user,
		    server_auth_types);
500

501
	/* Check for a redirect.
502
	 * Right now we only permit a redirect to the same hostname. */
503
	if ((parser->status_code == 301 ||
504 505
	     parser->status_code == 302 ||
	     (parser->status_code == 303 && get_verb == s->verb) ||
506 507
	     parser->status_code == 307 ||
	     parser->status_code == 308) &&
508
	    t->location) {
509

510
		if (gitno_connection_data_from_url(&t->server.url, t->location, s->service_url) < 0)
511
			return t->parse_error = PARSE_ERROR_GENERIC;
512 513 514 515 516 517 518 519 520 521

		/* 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;
Edward Thomson committed
522 523
		t->parse_error = PARSE_ERROR_REPLAY;
		return 0;
524 525
	}

526 527
	/* Check for a 200 HTTP status code. */
	if (parser->status_code != 200) {
528
		git_error_set(GIT_ERROR_NET,
529
			"unexpected HTTP status code: %d",
530 531
			parser->status_code);
		return t->parse_error = PARSE_ERROR_GENERIC;
532 533
	}

534 535
	/* The response must contain a Content-Type header. */
	if (!t->content_type) {
536
		git_error_set(GIT_ERROR_NET, "no Content-Type header in response");
537 538
		return t->parse_error = PARSE_ERROR_GENERIC;
	}
539

540 541 542 543 544 545 546 547 548 549 550 551 552 553
	/* The Content-Type header must match our expectation. */
	if (get_verb == s->verb)
		git_buf_printf(&buf,
			"application/x-git-%s-advertisement",
			ctx->s->service);
	else
		git_buf_printf(&buf,
			"application/x-git-%s-result",
			ctx->s->service);

	if (git_buf_oom(&buf))
		return t->parse_error = PARSE_ERROR_GENERIC;

	if (strcmp(t->content_type, git_buf_cstr(&buf))) {
554
		git_buf_dispose(&buf);
555
		git_error_set(GIT_ERROR_NET,
556
			"invalid Content-Type: %s",
557 558
			t->content_type);
		return t->parse_error = PARSE_ERROR_GENERIC;
559
	}
560

561
	git_buf_dispose(&buf);
562

563 564 565 566 567
	return 0;
}

static int on_message_complete(http_parser *parser)
{
568 569
	parser_context *ctx = (parser_context *) parser->data;
	http_subtransport *t = ctx->t;
570

571
	t->parse_finished = 1;
572

573 574 575
	return 0;
}

576
static int on_body_fill_buffer(http_parser *parser, const char *str, size_t len)
577
{
578 579
	parser_context *ctx = (parser_context *) parser->data;
	http_subtransport *t = ctx->t;
580

Edward Thomson committed
581 582 583 584 585 586 587
	/* If our goal is to replay the request (either an auth failure or
	 * a redirect) then don't bother buffering since we're ignoring the
	 * content anyway.
	 */
	if (t->parse_error == PARSE_ERROR_REPLAY)
		return 0;

588 589 590
	/* If there's no buffer set, we're explicitly ignoring the body. */
	if (ctx->buffer) {
		if (ctx->buf_size < len) {
591
			git_error_set(GIT_ERROR_NET, "can't fit data in the buffer");
592 593 594 595 596 597
			return t->parse_error = PARSE_ERROR_GENERIC;
		}

		memcpy(ctx->buffer, str, len);
		ctx->buffer += len;
		ctx->buf_size -= len;
598
	}
599

600
	*(ctx->bytes_read) += len;
601

602 603
	return 0;
}
604

605 606 607
static void clear_parser_state(http_subtransport *t)
{
	http_parser_init(&t->parser, HTTP_RESPONSE);
608
	gitno_buffer_setup_fromstream(t->server.stream,
609 610 611 612 613 614 615 616
		&t->parse_buffer,
		t->parse_buffer_data,
		sizeof(t->parse_buffer_data));

	t->last_cb = NONE;
	t->parse_error = 0;
	t->parse_finished = 0;

617
	git_buf_dispose(&t->parse_header_name);
618 619
	git_buf_init(&t->parse_header_name, 0);

620
	git_buf_dispose(&t->parse_header_value);
621 622 623 624 625
	git_buf_init(&t->parse_header_value, 0);

	git__free(t->content_type);
	t->content_type = NULL;

626 627 628
	git__free(t->content_length);
	t->content_length = NULL;

629 630 631
	git__free(t->location);
	t->location = NULL;

632 633
	git_vector_free_deep(&t->proxy.auth_challenges);
	git_vector_free_deep(&t->server.auth_challenges);
634 635
}

636
static int write_chunk(git_stream *io, const char *buffer, size_t len)
637 638 639 640
{
	git_buf buf = GIT_BUF_INIT;

	/* Chunk header */
641
	git_buf_printf(&buf, "%" PRIxZ "\r\n", len);
642 643 644 645

	if (git_buf_oom(&buf))
		return -1;

646
	if (git_stream__write_full(io, buf.ptr, buf.size, 0) < 0) {
647
		git_buf_dispose(&buf);
648 649 650
		return -1;
	}

651
	git_buf_dispose(&buf);
652 653

	/* Chunk body */
654
	if (len > 0 && git_stream__write_full(io, buffer, len, 0) < 0)
655 656 657
		return -1;

	/* Chunk footer */
658
	if (git_stream__write_full(io, "\r\n", 2, 0) < 0)
659 660 661 662 663
		return -1;

	return 0;
}

664 665 666 667 668 669
static int load_proxy_config(http_subtransport *t)
{
	int error;

	switch (t->owner->proxy.type) {
	case GIT_PROXY_NONE:
670 671
		return 0;

672 673 674 675
	case GIT_PROXY_AUTO:
		git__free(t->proxy_url);
		t->proxy_url = NULL;

676
		git_proxy_init_options(&t->proxy_opts, GIT_PROXY_OPTIONS_VERSION);
677

678
		if ((error = git_remote__get_http_proxy(t->owner->owner,
679
		    !!t->server.url.use_ssl, &t->proxy_url)) < 0)
680 681
			return error;

682 683 684
		if (!t->proxy_url)
			return 0;

685 686 687 688 689
		t->proxy_opts.type = GIT_PROXY_SPECIFIED;
		t->proxy_opts.url = t->proxy_url;
		t->proxy_opts.credentials = t->owner->proxy.credentials;
		t->proxy_opts.certificate_check = t->owner->proxy.certificate_check;
		t->proxy_opts.payload = t->owner->proxy.payload;
690
		break;
691

692
	case GIT_PROXY_SPECIFIED:
693
		memcpy(&t->proxy_opts, &t->owner->proxy, sizeof(git_proxy_options));
694 695 696 697 698
		break;

	default:
		assert(0);
		return -1;
699 700
	}

701 702 703 704
	if ((error = gitno_connection_data_from_url(&t->proxy.url, t->proxy_opts.url, NULL)) < 0)
		return error;

	if (t->proxy.url.use_ssl) {
705
		git_error_set(GIT_ERROR_NET, "SSL connections to proxy are not supported");
706 707 708 709
		return -1;
	}

	return error;
710 711
}

712 713 714 715 716 717 718 719
static int check_certificate(
	git_stream *stream,
	gitno_connection_data *url,
	int is_valid,
	git_transport_certificate_check_cb cert_cb,
	void *cert_cb_payload)
{
	git_cert *cert;
720
	git_error_state last_error = {0};
721 722 723 724 725
	int error;

	if ((error = git_stream_certificate(&cert, stream)) < 0)
		return error;

726
	git_error_state_capture(&last_error, GIT_ECERTIFICATE);
727

728
	error = cert_cb(cert, is_valid, url->host, cert_cb_payload);
729

730
	if (error == GIT_PASSTHROUGH && !is_valid)
731
		return git_error_state_restore(&last_error);
732 733
	else if (error == GIT_PASSTHROUGH)
		error = 0;
734 735
	else if (error && !git_error_last())
		git_error_set(GIT_ERROR_NET, "user rejected certificate for %s", url->host);
736

737
	git_error_state_free(&last_error);
738
	return error;
739 740
}

741 742 743 744 745 746 747 748
static int stream_connect(
	git_stream *stream,
	gitno_connection_data *url,
	git_transport_certificate_check_cb cert_cb,
	void *cb_payload)
{
	int error;

749
	GIT_ERROR_CHECK_VERSION(stream, GIT_STREAM_VERSION, "git_stream");
750 751 752 753 754 755 756 757 758 759 760 761

	error = git_stream_connect(stream);

	if (error && error != GIT_ECERTIFICATE)
		return error;

	if (git_stream_is_encrypted(stream) && cert_cb != NULL)
		error = check_certificate(stream, url, !error, cert_cb, cb_payload);

	return error;
}

762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788
static int gen_connect_req(git_buf *buf, http_subtransport *t)
{
	git_buf_printf(buf, "CONNECT %s:%s HTTP/1.1\r\n",
		t->server.url.host, t->server.url.port);

	git_buf_puts(buf, "User-Agent: ");
	git_http__user_agent(buf);
	git_buf_puts(buf, "\r\n");

	git_buf_printf(buf, "Host: %s\r\n", t->proxy.url.host);

	if (apply_credentials(buf, &t->proxy, AUTH_HEADER_PROXY) < 0)
		return -1;

	git_buf_puts(buf, "\r\n");

	return git_buf_oom(buf) ? -1 : 0;
}

static int proxy_headers_complete(http_parser *parser)
{
	parser_context *ctx = (parser_context *) parser->data;
	http_subtransport *t = ctx->t;
	int proxy_auth_types = 0;

	/* Enforce a reasonable cap on the number of replays */
	if (t->replay_count++ >= GIT_HTTP_REPLAY_MAX) {
789
		git_error_set(GIT_ERROR_NET, "too many redirects or authentication replays");
790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818
		return t->parse_error = PARSE_ERROR_GENERIC;
	}

	/* Both parse_header_name and parse_header_value are populated
	 * and ready for consumption. */
	if (VALUE == t->last_cb)
		if (on_header_ready(t) < 0)
			return t->parse_error = PARSE_ERROR_GENERIC;

	/*
	 * Capture authentication headers for the proxy or final endpoint,
	 * these may be 407/401 (authentication is not complete) or a 200
	 * (informing us that auth has completed).
	 */
	if (parse_authenticate_response(&t->proxy, &proxy_auth_types) < 0)
		return t->parse_error = PARSE_ERROR_GENERIC;

	/* Check for a proxy authentication failure. */
	if (parser->status_code == 407)
		return on_auth_required(&t->proxy.cred,
			parser,
			t->proxy_opts.url,
			SERVER_TYPE_PROXY,
			t->proxy_opts.credentials,
			t->proxy_opts.payload,
			t->proxy.url.user,
			proxy_auth_types);

	if (parser->status_code != 200) {
819
		git_error_set(GIT_ERROR_NET, "unexpected status code from proxy: %d",
820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855
			parser->status_code);
		return t->parse_error = PARSE_ERROR_GENERIC;
	}

	if (!t->content_length || strcmp(t->content_length, "0") == 0)
		t->parse_finished = 1;

	return 0;
}

static int proxy_connect(
	git_stream **out, git_stream *proxy_stream, http_subtransport *t)
{
	git_buf request = GIT_BUF_INIT;
	static http_parser_settings proxy_parser_settings = {0};
	size_t bytes_read = 0, bytes_parsed;
	parser_context ctx;
	int error;

	/* Use the parser settings only to parser headers. */
	proxy_parser_settings.on_header_field = on_header_field;
	proxy_parser_settings.on_header_value = on_header_value;
	proxy_parser_settings.on_headers_complete = proxy_headers_complete;
	proxy_parser_settings.on_message_complete = on_message_complete;

replay:
	clear_parser_state(t);

	gitno_buffer_setup_fromstream(proxy_stream,
		&t->parse_buffer,
		t->parse_buffer_data,
		sizeof(t->parse_buffer_data));

	if ((error = gen_connect_req(&request, t)) < 0)
		goto done;

856 857
	if ((error = git_stream__write_full(proxy_stream, request.ptr,
					    request.size, 0)) < 0)
858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888
		goto done;

	git_buf_dispose(&request);

	while (!bytes_read && !t->parse_finished) {
		t->parse_buffer.offset = 0;

		if ((error = gitno_recv(&t->parse_buffer)) < 0)
			goto done;

		/*
		 * This call to http_parser_execute will invoke the on_*
		 * callbacks.  Since we don't care about the body of the response,
		 * we can set our buffer to NULL.
		 */
		ctx.t = t;
		ctx.s = NULL;
		ctx.buffer = NULL;
		ctx.buf_size = 0;
		ctx.bytes_read = &bytes_read;

		/* Set the context, call the parser, then unset the context. */
		t->parser.data = &ctx;

		bytes_parsed = http_parser_execute(&t->parser,
			&proxy_parser_settings, t->parse_buffer.data, t->parse_buffer.offset);

		t->parser.data = NULL;

		/* Ensure that we didn't get a redirect; unsupported. */
		if (t->location) {
889
			git_error_set(GIT_ERROR_NET, "proxy server sent unsupported redirect during CONNECT");
890 891 892 893 894 895 896 897 898 899 900 901 902 903
			error = -1;
			goto done;
		}

		/* Replay the request with authentication headers. */
		if (PARSE_ERROR_REPLAY == t->parse_error)
			goto replay;

		if (t->parse_error < 0) {
			error = t->parse_error == PARSE_ERROR_EXT ? PARSE_ERROR_EXT : -1;
			goto done;
		}

		if (bytes_parsed != t->parse_buffer.offset) {
904
			git_error_set(GIT_ERROR_NET,
905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927
				"HTTP parser error: %s",
				http_errno_description((enum http_errno)t->parser.http_errno));
			error = -1;
			goto done;
		}
	}

	if ((error = git_tls_stream_wrap(out, proxy_stream, t->server.url.host)) == 0)
		error = stream_connect(*out, &t->server.url,
		    t->owner->certificate_check_cb,
			t->owner->message_cb_payload);

	/*
	 * Since we've connected via a HTTPS proxy tunnel, we don't behave
	 * as if we have an HTTP proxy.
	 */
	t->proxy_opts.type = GIT_PROXY_NONE;
	t->replay_count = 0;

done:
	return error;
}

928 929
static int http_connect(http_subtransport *t)
{
930
	gitno_connection_data *url;
931
	git_stream *proxy_stream = NULL, *stream = NULL;
932 933
	git_transport_certificate_check_cb cert_cb;
	void *cb_payload;
934
	int error;
935 936 937

	if (t->connected &&
		http_should_keep_alive(&t->parser) &&
Edward Thomson committed
938
		t->parse_finished)
939 940
		return 0;

941 942 943
	if ((error = load_proxy_config(t)) < 0)
		return error;

944 945 946 947
	if (t->server.stream) {
		git_stream_close(t->server.stream);
		git_stream_free(t->server.stream);
		t->server.stream = NULL;
948
	}
949

950 951 952 953 954 955 956 957
	if (t->proxy.stream) {
		git_stream_close(t->proxy.stream);
		git_stream_free(t->proxy.stream);
		t->proxy.stream = NULL;
	}

	t->connected = 0;

958 959 960 961 962 963 964 965 966
	if (t->proxy_opts.type == GIT_PROXY_SPECIFIED) {
		url = &t->proxy.url;
		cert_cb = t->proxy_opts.certificate_check;
		cb_payload = t->proxy_opts.payload;
	} else {
		url = &t->server.url;
		cert_cb = t->owner->certificate_check_cb;
		cb_payload = t->owner->message_cb_payload;
	}
967

968 969
	if (url->use_ssl)
		error = git_tls_stream_new(&stream, url->host, url->port);
970
	else
971
		error = git_socket_stream_new(&stream, url->host, url->port);
972

973
	if (error < 0)
974
		goto on_error;
975

976
	if ((error = stream_connect(stream, url, cert_cb, cb_payload)) < 0)
977
		goto on_error;
978

979 980 981 982 983 984 985 986 987 988 989 990 991 992 993
	/*
	 * At this point we have a connection to the remote server or to
	 * a proxy.  If it's a proxy and the remote server is actually
	 * an HTTPS connection, then we need to build a CONNECT tunnel.
	 */
	if (t->proxy_opts.type == GIT_PROXY_SPECIFIED &&
		t->server.url.use_ssl) {
		proxy_stream = stream;
		stream = NULL;

		if ((error = proxy_connect(&stream, proxy_stream, t)) < 0)
			goto on_error;
	}

	t->proxy.stream = proxy_stream;
994 995
	t->server.stream = stream;
	t->connected = 1;
996
	t->replay_count = 0;
997
	return 0;
998

999 1000 1001 1002
on_error:
	if (stream) {
		git_stream_close(stream);
		git_stream_free(stream);
1003
	}
1004

1005 1006 1007 1008 1009
	if (proxy_stream) {
		git_stream_close(proxy_stream);
		git_stream_free(proxy_stream);
	}

1010
	return error;
1011 1012
}

1013 1014 1015 1016 1017
static int http_stream_read(
	git_smart_subtransport_stream *stream,
	char *buffer,
	size_t buf_size,
	size_t *bytes_read)
1018
{
1019
	http_stream *s = (http_stream *)stream;
1020
	http_subtransport *t = OWNING_SUBTRANSPORT(s);
1021
	parser_context ctx;
1022
	size_t bytes_parsed;
1023

1024
replay:
1025
	*bytes_read = 0;
1026

1027
	assert(t->connected);
1028

1029
	if (!s->sent_request) {
1030 1031
		git_buf request = GIT_BUF_INIT;

1032 1033
		clear_parser_state(t);

1034
		if (gen_request(&request, s, 0) < 0)
1035
			return -1;
1036

1037 1038
		if (git_stream__write_full(t->server.stream, request.ptr,
					   request.size, 0) < 0) {
1039
			git_buf_dispose(&request);
1040 1041 1042
			return -1;
		}

1043
		git_buf_dispose(&request);
1044

1045
		s->sent_request = 1;
1046 1047
	}

1048 1049 1050
	if (!s->received_response) {
		if (s->chunked) {
			assert(s->verb == post_verb);
1051

1052 1053
			/* Flush, if necessary */
			if (s->chunk_buffer_len > 0 &&
1054
				write_chunk(t->server.stream,
1055
				    s->chunk_buffer, s->chunk_buffer_len) < 0)
1056
				return -1;
1057

1058
			s->chunk_buffer_len = 0;
1059

1060
			/* Write the final chunk. */
1061 1062
			if (git_stream__write_full(t->server.stream,
						   "0\r\n\r\n", 5, 0) < 0)
1063 1064 1065 1066
				return -1;
		}

		s->received_response = 1;
1067 1068
	}

1069
	while (!*bytes_read && !t->parse_finished) {
1070
		size_t data_offset;
1071
		int error;
1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087

		/*
		 * Make the parse_buffer think it's as full of data as
		 * the buffer, so it won't try to recv more data than
		 * we can put into it.
		 *
		 * data_offset is the actual data offset from which we
		 * should tell the parser to start reading.
		 */
		if (buf_size >= t->parse_buffer.len) {
			t->parse_buffer.offset = 0;
		} else {
			t->parse_buffer.offset = t->parse_buffer.len - buf_size;
		}

		data_offset = t->parse_buffer.offset;
1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107

		if (gitno_recv(&t->parse_buffer) < 0)
			return -1;

		/* This call to http_parser_execute will result in invocations of the
		 * on_* family of callbacks. The most interesting of these is
		 * on_body_fill_buffer, which is called when data is ready to be copied
		 * into the target buffer. We need to marshal the buffer, buf_size, and
		 * bytes_read parameters to this callback. */
		ctx.t = t;
		ctx.s = s;
		ctx.buffer = buffer;
		ctx.buf_size = buf_size;
		ctx.bytes_read = bytes_read;

		/* Set the context, call the parser, then unset the context. */
		t->parser.data = &ctx;

		bytes_parsed = http_parser_execute(&t->parser,
			&t->settings,
1108 1109
			t->parse_buffer.data + data_offset,
			t->parse_buffer.offset - data_offset);
1110 1111 1112 1113 1114 1115 1116

		t->parser.data = NULL;

		/* If there was a handled authentication failure, then parse_error
		 * will have signaled us that we should replay the request. */
		if (PARSE_ERROR_REPLAY == t->parse_error) {
			s->sent_request = 0;
1117

1118 1119
			if ((error = http_connect(t)) < 0)
				return error;
1120

1121 1122 1123
			goto replay;
		}

1124 1125 1126 1127
		if (t->parse_error == PARSE_ERROR_EXT) {
			return t->error;
		}

1128 1129 1130
		if (t->parse_error < 0)
			return -1;

1131
		if (bytes_parsed != t->parse_buffer.offset - data_offset) {
1132
			git_error_set(GIT_ERROR_NET,
1133 1134 1135 1136 1137
				"HTTP parser error: %s",
				http_errno_description((enum http_errno)t->parser.http_errno));
			return -1;
		}
	}
1138

1139
	return 0;
1140 1141
}

1142
static int http_stream_write_chunked(
1143 1144 1145
	git_smart_subtransport_stream *stream,
	const char *buffer,
	size_t len)
1146
{
1147
	http_stream *s = (http_stream *)stream;
1148
	http_subtransport *t = OWNING_SUBTRANSPORT(s);
1149

1150
	assert(t->connected);
1151

1152
	/* Send the request, if necessary */
1153
	if (!s->sent_request) {
1154 1155
		git_buf request = GIT_BUF_INIT;

1156 1157
		clear_parser_state(t);

1158
		if (gen_request(&request, s, 0) < 0)
1159
			return -1;
1160

1161 1162
		if (git_stream__write_full(t->server.stream, request.ptr,
					   request.size, 0) < 0) {
1163
			git_buf_dispose(&request);
1164 1165
			return -1;
		}
1166

1167
		git_buf_dispose(&request);
1168

1169 1170
		s->sent_request = 1;
	}
1171

1172 1173 1174
	if (len > CHUNK_SIZE) {
		/* Flush, if necessary */
		if (s->chunk_buffer_len > 0) {
1175
			if (write_chunk(t->server.stream,
1176
			    s->chunk_buffer, s->chunk_buffer_len) < 0)
1177 1178 1179 1180 1181 1182
				return -1;

			s->chunk_buffer_len = 0;
		}

		/* Write chunk directly */
1183
		if (write_chunk(t->server.stream, buffer, len) < 0)
1184 1185 1186 1187
			return -1;
	}
	else {
		/* Append as much to the buffer as we can */
1188
		int count = min(CHUNK_SIZE - s->chunk_buffer_len, len);
1189 1190

		if (!s->chunk_buffer)
1191
			s->chunk_buffer = git__malloc(CHUNK_SIZE);
1192 1193 1194 1195 1196 1197 1198 1199

		memcpy(s->chunk_buffer + s->chunk_buffer_len, buffer, count);
		s->chunk_buffer_len += count;
		buffer += count;
		len -= count;

		/* Is the buffer full? If so, then flush */
		if (CHUNK_SIZE == s->chunk_buffer_len) {
1200
			if (write_chunk(t->server.stream,
1201
			    s->chunk_buffer, s->chunk_buffer_len) < 0)
1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227
				return -1;

			s->chunk_buffer_len = 0;

			if (len > 0) {
				memcpy(s->chunk_buffer, buffer, len);
				s->chunk_buffer_len = len;
			}
		}
	}

	return 0;
}

static int http_stream_write_single(
	git_smart_subtransport_stream *stream,
	const char *buffer,
	size_t len)
{
	http_stream *s = (http_stream *)stream;
	http_subtransport *t = OWNING_SUBTRANSPORT(s);
	git_buf request = GIT_BUF_INIT;

	assert(t->connected);

	if (s->sent_request) {
1228
		git_error_set(GIT_ERROR_NET, "subtransport configured for only one write");
1229 1230 1231 1232 1233
		return -1;
	}

	clear_parser_state(t);

1234
	if (gen_request(&request, s, len) < 0)
1235 1236
		return -1;

1237
	if (git_stream__write_full(t->server.stream, request.ptr, request.size, 0) < 0)
1238 1239
		goto on_error;

1240
	if (len && git_stream__write_full(t->server.stream, buffer, len, 0) < 0)
1241 1242
		goto on_error;

1243
	git_buf_dispose(&request);
1244 1245
	s->sent_request = 1;

1246 1247 1248
	return 0;

on_error:
1249
	git_buf_dispose(&request);
1250 1251
	return -1;
}
1252

1253 1254 1255
static void http_stream_free(git_smart_subtransport_stream *stream)
{
	http_stream *s = (http_stream *)stream;
1256

1257 1258 1259
	if (s->chunk_buffer)
		git__free(s->chunk_buffer);

1260 1261 1262
	if (s->redirect_url)
		git__free(s->redirect_url);

1263 1264
	git__free(s);
}
1265

1266 1267
static int http_stream_alloc(http_subtransport *t,
	git_smart_subtransport_stream **stream)
1268
{
1269
	http_stream *s;
1270

1271 1272
	if (!stream)
		return -1;
1273

1274
	s = git__calloc(sizeof(http_stream), 1);
1275
	GIT_ERROR_CHECK_ALLOC(s);
1276

1277 1278
	s->parent.subtransport = &t->parent;
	s->parent.read = http_stream_read;
1279
	s->parent.write = http_stream_write_single;
1280
	s->parent.free = http_stream_free;
1281

1282 1283
	*stream = (git_smart_subtransport_stream *)s;
	return 0;
1284 1285
}

1286 1287 1288
static int http_uploadpack_ls(
	http_subtransport *t,
	git_smart_subtransport_stream **stream)
1289
{
1290
	http_stream *s;
1291

1292
	if (http_stream_alloc(t, stream) < 0)
1293 1294
		return -1;

1295
	s = (http_stream *)*stream;
1296

1297 1298 1299
	s->service = upload_pack_service;
	s->service_url = upload_pack_ls_service_url;
	s->verb = get_verb;
1300

1301
	return 0;
1302 1303
}

1304 1305 1306
static int http_uploadpack(
	http_subtransport *t,
	git_smart_subtransport_stream **stream)
1307
{
1308
	http_stream *s;
1309

1310
	if (http_stream_alloc(t, stream) < 0)
1311
		return -1;
1312

1313
	s = (http_stream *)*stream;
1314

1315 1316 1317
	s->service = upload_pack_service;
	s->service_url = upload_pack_service_url;
	s->verb = post_verb;
1318

1319
	return 0;
1320 1321
}

1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361
static int http_receivepack_ls(
	http_subtransport *t,
	git_smart_subtransport_stream **stream)
{
	http_stream *s;

	if (http_stream_alloc(t, stream) < 0)
		return -1;

	s = (http_stream *)*stream;

	s->service = receive_pack_service;
	s->service_url = receive_pack_ls_service_url;
	s->verb = get_verb;

	return 0;
}

static int http_receivepack(
	http_subtransport *t,
	git_smart_subtransport_stream **stream)
{
	http_stream *s;

	if (http_stream_alloc(t, stream) < 0)
		return -1;

	s = (http_stream *)*stream;

	/* Use Transfer-Encoding: chunked for this request */
	s->chunked = 1;
	s->parent.write = http_stream_write_chunked;

	s->service = receive_pack_service;
	s->service_url = receive_pack_service_url;
	s->verb = post_verb;

	return 0;
}

1362 1363
static int http_action(
	git_smart_subtransport_stream **stream,
1364
	git_smart_subtransport *subtransport,
1365 1366
	const char *url,
	git_smart_service_t action)
1367
{
1368
	http_subtransport *t = (http_subtransport *)subtransport;
1369
	int ret;
1370

1371
	assert(stream);
1372

1373 1374 1375 1376 1377 1378 1379
	/*
	 * If we've seen a redirect then preserve the location that we've
	 * been given.  This is important to continue authorization against
	 * the redirect target, not the user-given source; the endpoint may
	 * have redirected us from HTTP->HTTPS and is using an auth mechanism
	 * that would be insecure in plaintext (eg, HTTP Basic).
	 */
1380 1381
	if ((!t->server.url.host || !t->server.url.port || !t->server.url.path) &&
	    (ret = gitno_connection_data_from_url(&t->server.url, url, NULL)) < 0)
1382 1383
		return ret;

1384
	assert(t->server.url.host && t->server.url.port && t->server.url.path);
1385

1386
	if ((ret = http_connect(t)) < 0)
1387
		return ret;
1388

1389 1390 1391
	switch (action) {
	case GIT_SERVICE_UPLOADPACK_LS:
		return http_uploadpack_ls(t, stream);
1392

1393 1394
	case GIT_SERVICE_UPLOADPACK:
		return http_uploadpack(t, stream);
1395

1396 1397
	case GIT_SERVICE_RECEIVEPACK_LS:
		return http_receivepack_ls(t, stream);
1398

1399 1400
	case GIT_SERVICE_RECEIVEPACK:
		return http_receivepack(t, stream);
1401
	}
1402

1403 1404 1405 1406
	*stream = NULL;
	return -1;
}

1407
static void free_auth_contexts(git_vector *contexts)
1408
{
1409 1410
	git_http_auth_context *context;
	size_t i;
1411

1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423
	git_vector_foreach(contexts, i, context) {
		if (context->free)
			context->free(context);
	}

	git_vector_clear(contexts);
}

static int http_close(git_smart_subtransport *subtransport)
{
	http_subtransport *t = (http_subtransport *) subtransport;

1424 1425
	clear_parser_state(t);

1426 1427
	t->connected = 0;

1428 1429 1430 1431
	if (t->server.stream) {
		git_stream_close(t->server.stream);
		git_stream_free(t->server.stream);
		t->server.stream = NULL;
1432
	}
1433

1434 1435 1436 1437 1438 1439
	if (t->proxy.stream) {
		git_stream_close(t->proxy.stream);
		git_stream_free(t->proxy.stream);
		t->proxy.stream = NULL;
	}

1440 1441 1442 1443
	free_cred(&t->server.cred);
	free_cred(&t->server.url_cred);
	free_cred(&t->proxy.cred);
	free_cred(&t->proxy.url_cred);
1444

1445 1446
	free_auth_contexts(&t->server.auth_contexts);
	free_auth_contexts(&t->proxy.auth_contexts);
1447

1448 1449
	gitno_connection_data_free_ptrs(&t->server.url);
	memset(&t->server.url, 0x0, sizeof(gitno_connection_data));
1450

1451 1452
	gitno_connection_data_free_ptrs(&t->proxy.url);
	memset(&t->proxy.url, 0x0, sizeof(gitno_connection_data));
1453 1454 1455 1456

	git__free(t->proxy_url);
	t->proxy_url = NULL;

1457 1458 1459 1460 1461 1462 1463 1464 1465
	return 0;
}

static void http_free(git_smart_subtransport *subtransport)
{
	http_subtransport *t = (http_subtransport *) subtransport;

	http_close(subtransport);

1466 1467
	git_vector_free(&t->server.auth_contexts);
	git_vector_free(&t->proxy.auth_contexts);
1468
	git__free(t);
1469 1470
}

1471
int git_smart_subtransport_http(git_smart_subtransport **out, git_transport *owner, void *param)
1472
{
1473
	http_subtransport *t;
1474

1475 1476
	GIT_UNUSED(param);

1477 1478
	if (!out)
		return -1;
1479

1480
	t = git__calloc(sizeof(http_subtransport), 1);
1481
	GIT_ERROR_CHECK_ALLOC(t);
1482

1483
	t->owner = (transport_smart *)owner;
1484
	t->parent.action = http_action;
1485
	t->parent.close = http_close;
1486
	t->parent.free = http_free;
1487

1488 1489 1490 1491 1492
	t->settings.on_header_field = on_header_field;
	t->settings.on_header_value = on_header_value;
	t->settings.on_headers_complete = on_headers_complete;
	t->settings.on_body = on_body_fill_buffer;
	t->settings.on_message_complete = on_message_complete;
1493

1494
	*out = (git_smart_subtransport *) t;
1495 1496
	return 0;
}
1497 1498

#endif /* !GIT_WINHTTP */