http.c 24.8 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
#ifndef GIT_WINHTTP

9 10 11
#include "git2.h"
#include "http_parser.h"
#include "buffer.h"
12
#include "netops.h"
13
#include "global.h"
14
#include "remote.h"
15
#include "smart.h"
16 17
#include "auth.h"
#include "auth_negotiate.h"
18
#include "tls_stream.h"
19
#include "socket_stream.h"
20
#include "curl_stream.h"
21

22 23 24 25
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 },
};
26

27 28 29
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";
30 31 32
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";
33 34 35 36
static const char *get_verb = "GET";
static const char *post_verb = "POST";

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

38 39
#define PARSE_ERROR_GENERIC	-1
#define PARSE_ERROR_REPLAY	-2
40 41
/** Look at the user field */
#define PARSE_ERROR_EXT         -3
42

43 44
#define CHUNK_SIZE	4096

45
enum last_cb {
46 47 48
	NONE,
	FIELD,
	VALUE
49
};
50

51
typedef struct {
52 53 54
	git_smart_subtransport_stream parent;
	const char *service;
	const char *service_url;
55
	char *redirect_url;
56
	const char *verb;
57 58 59 60
	char *chunk_buffer;
	unsigned chunk_buffer_len;
	unsigned sent_request : 1,
		received_response : 1,
61 62
		chunked : 1,
		redirect_count : 3;
63 64 65 66
} http_stream;

typedef struct {
	git_smart_subtransport parent;
67
	transport_smart *owner;
68
	git_stream *io;
69 70
	gitno_connection_data connection_data;
	bool connected;
71 72

	/* Parser structures */
73
	http_parser parser;
74 75
	http_parser_settings settings;
	gitno_buffer parse_buffer;
76 77
	git_buf parse_header_name;
	git_buf parse_header_value;
78
	char parse_buffer_data[NETIO_BUFSIZE];
79
	char *content_type;
80
	char *location;
81
	git_vector www_authenticate;
82
	enum last_cb last_cb;
83
	int parse_error;
84
	int error;
85
	unsigned parse_finished : 1;
86

87 88 89 90
	/* Authentication */
	git_cred *cred;
	git_cred *url_cred;
	git_vector auth_contexts;
91 92 93 94 95 96 97 98 99 100 101
} 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;
102

103
static bool credtype_match(git_http_auth_scheme *scheme, void *data)
104
{
105
	unsigned int credtype = *(unsigned int *)data;
106

107
	return !!(scheme->credtypes & credtype);
108 109
}

110
static bool challenge_match(git_http_auth_scheme *scheme, void *data)
111
{
112 113 114 115 116
	const char *scheme_name = scheme->name;
	const char *challenge = (const char *)data;
	size_t scheme_len;

	scheme_len = strlen(scheme_name);
117
	return (strncasecmp(challenge, scheme_name, scheme_len) == 0 &&
118
		(challenge[scheme_len] == '\0' || challenge[scheme_len] == ' '));
119 120
}

121 122 123 124 125
static int auth_context_match(
	git_http_auth_context **out,
	http_subtransport *t,
	bool (*scheme_match)(git_http_auth_scheme *scheme, void *data),
	void *data)
126
{
127 128
	git_http_auth_scheme *scheme = NULL;
	git_http_auth_context *context = NULL, *c;
129 130
	size_t i;

131
	*out = NULL;
132

133 134 135 136
	for (i = 0; i < ARRAY_SIZE(auth_schemes); i++) {
		if (scheme_match(&auth_schemes[i], data)) {
			scheme = &auth_schemes[i];
			break;
137 138 139
		}
	}

140 141
	if (!scheme)
		return 0;
142

143 144 145 146 147 148
	/* See if authentication has already started for this scheme */
	git_vector_foreach(&t->auth_contexts, i, c) {
		if (c->type == scheme->type) {
			context = c;
			break;
		}
149 150
	}

151 152 153 154 155 156 157 158
	if (!context) {
		if (scheme->init_context(&context, &t->connection_data) < 0)
			return -1;
		else if (!context)
			return 0;
		else if (git_vector_insert(&t->auth_contexts, context) < 0)
			return -1;
	}
159

160
	*out = context;
161 162 163 164

	return 0;
}

165
static int apply_credentials(git_buf *buf, http_subtransport *t)
166
{
167 168 169 170 171 172 173 174 175
	git_cred *cred = t->cred;
	git_http_auth_context *context;

	/* Apply the credentials given to us in the URL */
	if (!cred && t->connection_data.user && t->connection_data.pass) {
		if (!t->url_cred &&
			git_cred_userpass_plaintext_new(&t->url_cred,
				t->connection_data.user, t->connection_data.pass) < 0)
			return -1;
176

177
		cred = t->url_cred;
178 179
	}

180 181
	if (!cred)
		return 0;
182

183 184
	/* Get or create a context for the best scheme for this cred type */
	if (auth_context_match(&context, t, credtype_match, &cred->credtype) < 0)
185 186
		return -1;

187
	return context->next_token(buf, context, cred);
188 189
}

190 191 192 193 194 195 196 197 198 199
static const char *user_agent(void)
{
	const char *custom = git_libgit2__user_agent();

	if (custom)
		return custom;

	return "libgit2 " LIBGIT2_VERSION;
}

200 201
static int gen_request(
	git_buf *buf,
202 203
	http_stream *s,
	size_t content_length)
204
{
205
	http_subtransport *t = OWNING_SUBTRANSPORT(s);
206
	const char *path = t->connection_data.path ? t->connection_data.path : "/";
207
	size_t i;
208

209
	git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, path, s->service_url);
210

211
	git_buf_printf(buf, "User-Agent: git/2.0 (%s)\r\n", user_agent());
212
	git_buf_printf(buf, "Host: %s\r\n", t->connection_data.host);
213 214 215 216 217 218 219 220 221 222

	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
223
		git_buf_puts(buf, "Accept: */*\r\n");
224

225 226 227
	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]);
228 229
	}

230
	/* Apply credentials to the request */
231 232
	if (apply_credentials(buf, t) < 0)
		return -1;
233

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

236
	if (git_buf_oom(buf))
237
		return -1;
238 239

	return 0;
240 241
}

242
static int parse_authenticate_response(
243
	git_vector *www_authenticate,
244 245
	http_subtransport *t,
	int *allowed_types)
246
{
247 248 249
	git_http_auth_context *context;
	char *challenge;
	size_t i;
250

251 252 253 254 255
	git_vector_foreach(www_authenticate, i, challenge) {
		if (auth_context_match(&context, t, challenge_match, challenge) < 0)
			return -1;
		else if (!context)
			continue;
256

257 258 259
		if (context->set_challenge &&
			context->set_challenge(context, challenge) < 0)
			return -1;
260

261
		*allowed_types |= context->credtypes;
262 263 264 265 266 267 268 269 270 271
	}

	return 0;
}

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

272 273 274 275 276
	if (!strcasecmp("Content-Type", git_buf_cstr(name))) {
		if (!t->content_type) {
			t->content_type = git__strdup(git_buf_cstr(value));
			GITERR_CHECK_ALLOC(t->content_type);
		}
277
	}
278
	else if (!strcasecmp("WWW-Authenticate", git_buf_cstr(name))) {
279
		char *dup = git__strdup(git_buf_cstr(value));
280
		GITERR_CHECK_ALLOC(dup);
281

282 283
		git_vector_insert(&t->www_authenticate, dup);
	}
284 285
	else if (!strcasecmp("Location", git_buf_cstr(name))) {
		if (!t->location) {
286
			t->location = git__strdup(git_buf_cstr(value));
287 288 289
			GITERR_CHECK_ALLOC(t->location);
		}
	}
290 291 292 293

	return 0;
}

294 295
static int on_header_field(http_parser *parser, const char *str, size_t len)
{
296 297
	parser_context *ctx = (parser_context *) parser->data;
	http_subtransport *t = ctx->t;
298

299 300 301 302 303
	/* 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;
304

305 306
	if (NONE == t->last_cb || VALUE == t->last_cb)
		git_buf_clear(&t->parse_header_name);
307

308 309
	if (git_buf_put(&t->parse_header_name, str, len) < 0)
		return t->parse_error = PARSE_ERROR_GENERIC;
310

311
	t->last_cb = FIELD;
312
	return 0;
313 314 315 316
}

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

320
	assert(NONE != t->last_cb);
321

322 323
	if (FIELD == t->last_cb)
		git_buf_clear(&t->parse_header_value);
324

325 326
	if (git_buf_put(&t->parse_header_value, str, len) < 0)
		return t->parse_error = PARSE_ERROR_GENERIC;
327

328
	t->last_cb = VALUE;
329
	return 0;
330 331 332 333
}

static int on_headers_complete(http_parser *parser)
{
334 335
	parser_context *ctx = (parser_context *) parser->data;
	http_subtransport *t = ctx->t;
336 337
	http_stream *s = ctx->s;
	git_buf buf = GIT_BUF_INIT;
338
	int error = 0, no_callback = 0, allowed_auth_types = 0;
339

340 341 342 343 344 345
	/* 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;

346 347 348 349
	/* Capture authentication headers which may be a 401 (authentication
	 * is not complete) or a 200 (simply informing us that auth *is*
	 * complete.)
	 */
350 351
	if (parse_authenticate_response(&t->www_authenticate, t,
			&allowed_auth_types) < 0)
352 353 354 355
		return t->parse_error = PARSE_ERROR_GENERIC;

	/* Check for an authentication failure. */
	if (parser->status_code == 401 && get_verb == s->verb) {
356
		if (!t->owner->cred_acquire_cb) {
357 358
			no_callback = 1;
		} else {
359
			if (allowed_auth_types) {
360 361 362 363
				if (t->cred) {
					t->cred->free(t->cred);
					t->cred = NULL;
				}
364 365 366 367

				error = t->owner->cred_acquire_cb(&t->cred,
								  t->owner->url,
								  t->connection_data.user,
368
								  allowed_auth_types,
369 370 371 372 373
								  t->owner->cred_acquire_payload);

				if (error == GIT_PASSTHROUGH) {
					no_callback = 1;
				} else if (error < 0) {
374 375
					t->error = error;
					return t->parse_error = PARSE_ERROR_EXT;
376 377 378
				} else {
					assert(t->cred);

379 380 381 382 383
					if (!(t->cred->credtype & allowed_auth_types)) {
						giterr_set(GITERR_NET, "credentials callback returned an invalid cred type");
						return t->parse_error = PARSE_ERROR_GENERIC;
					}

384
					/* Successfully acquired a credential. */
Edward Thomson committed
385 386
					t->parse_error = PARSE_ERROR_REPLAY;
					return 0;
387 388 389
				}
			}
		}
390

391 392
		if (no_callback) {
			giterr_set(GITERR_NET, "authentication required but no callback set");
393 394
			return t->parse_error = PARSE_ERROR_GENERIC;
		}
395 396
	}

397
	/* Check for a redirect.
398
	 * Right now we only permit a redirect to the same hostname. */
399
	if ((parser->status_code == 301 ||
400 401 402 403
	     parser->status_code == 302 ||
	     (parser->status_code == 303 && get_verb == s->verb) ||
	     parser->status_code == 307) &&
	    t->location) {
404 405

		if (s->redirect_count >= 7) {
406
			giterr_set(GITERR_NET, "too many redirects");
407 408 409
			return t->parse_error = PARSE_ERROR_GENERIC;
		}

410
		if (gitno_connection_data_from_url(&t->connection_data, t->location, s->service_url) < 0)
411
			return t->parse_error = PARSE_ERROR_GENERIC;
412 413 414 415 416 417 418 419 420 421 422 423

		/* Set the redirect URL on the stream. This is a transfer of
		 * ownership of the memory. */
		if (s->redirect_url)
			git__free(s->redirect_url);

		s->redirect_url = t->location;
		t->location = NULL;

		t->connected = 0;
		s->redirect_count++;

Edward Thomson committed
424 425
		t->parse_error = PARSE_ERROR_REPLAY;
		return 0;
426 427
	}

428 429 430
	/* Check for a 200 HTTP status code. */
	if (parser->status_code != 200) {
		giterr_set(GITERR_NET,
431
			"unexpected HTTP status code: %d",
432 433
			parser->status_code);
		return t->parse_error = PARSE_ERROR_GENERIC;
434 435
	}

436 437
	/* The response must contain a Content-Type header. */
	if (!t->content_type) {
438
		giterr_set(GITERR_NET, "no Content-Type header in response");
439 440
		return t->parse_error = PARSE_ERROR_GENERIC;
	}
441

442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
	/* 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))) {
		git_buf_free(&buf);
		giterr_set(GITERR_NET,
458
			"invalid Content-Type: %s",
459 460
			t->content_type);
		return t->parse_error = PARSE_ERROR_GENERIC;
461
	}
462

463 464
	git_buf_free(&buf);

465 466 467 468 469
	return 0;
}

static int on_message_complete(http_parser *parser)
{
470 471
	parser_context *ctx = (parser_context *) parser->data;
	http_subtransport *t = ctx->t;
472

473
	t->parse_finished = 1;
474

475 476 477
	return 0;
}

478
static int on_body_fill_buffer(http_parser *parser, const char *str, size_t len)
479
{
480 481
	parser_context *ctx = (parser_context *) parser->data;
	http_subtransport *t = ctx->t;
482

Edward Thomson committed
483 484 485 486 487 488 489
	/* 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;

490
	if (ctx->buf_size < len) {
491
		giterr_set(GITERR_NET, "can't fit data in the buffer");
492
		return t->parse_error = PARSE_ERROR_GENERIC;
493
	}
494

495 496 497 498
	memcpy(ctx->buffer, str, len);
	*(ctx->bytes_read) += len;
	ctx->buffer += len;
	ctx->buf_size -= len;
499

500 501
	return 0;
}
502

503 504 505
static void clear_parser_state(http_subtransport *t)
{
	http_parser_init(&t->parser, HTTP_RESPONSE);
506
	gitno_buffer_setup_fromstream(t->io,
507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
		&t->parse_buffer,
		t->parse_buffer_data,
		sizeof(t->parse_buffer_data));

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

	git_buf_free(&t->parse_header_name);
	git_buf_init(&t->parse_header_name, 0);

	git_buf_free(&t->parse_header_value);
	git_buf_init(&t->parse_header_value, 0);

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

524 525 526
	git__free(t->location);
	t->location = NULL;

527
	git_vector_free_deep(&t->www_authenticate);
528 529
}

530
static int write_chunk(git_stream *io, const char *buffer, size_t len)
531 532 533 534
{
	git_buf buf = GIT_BUF_INIT;

	/* Chunk header */
535
	git_buf_printf(&buf, "%" PRIxZ "\r\n", len);
536 537 538 539

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

540
	if (git_stream_write(io, buf.ptr, buf.size, 0) < 0) {
541 542 543 544 545 546 547
		git_buf_free(&buf);
		return -1;
	}

	git_buf_free(&buf);

	/* Chunk body */
548
	if (len > 0 && git_stream_write(io, buffer, len, 0) < 0)
549 550 551
		return -1;

	/* Chunk footer */
552
	if (git_stream_write(io, "\r\n", 2, 0) < 0)
553 554 555 556 557
		return -1;

	return 0;
}

558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
static int apply_proxy_config(http_subtransport *t)
{
	int error;
	git_proxy_t proxy_type;

	if (!git_stream_supports_proxy(t->io))
		return 0;

	proxy_type = t->owner->proxy.type;

	if (proxy_type == GIT_PROXY_NONE)
		return 0;

	if (proxy_type == GIT_PROXY_AUTO) {
		char *url;
		git_proxy_options opts = GIT_PROXY_OPTIONS_INIT;

		if ((error = git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &url)) < 0)
			return error;

578
		opts.type = GIT_PROXY_SPECIFIED;
579 580 581 582 583 584 585 586 587 588
		opts.url = url;
		error = git_stream_set_proxy(t->io, &opts);
		git__free(url);

		return error;
	}

	return git_stream_set_proxy(t->io, &t->owner->proxy);
}

589 590
static int http_connect(http_subtransport *t)
{
591
	int error;
592 593 594

	if (t->connected &&
		http_should_keep_alive(&t->parser) &&
Edward Thomson committed
595
		t->parse_finished)
596 597
		return 0;

598 599 600 601
	if (t->io) {
		git_stream_close(t->io);
		git_stream_free(t->io);
		t->io = NULL;
602
		t->connected = 0;
603
	}
604

605
	if (t->connection_data.use_ssl) {
606
		error = git_tls_stream_new(&t->io, t->connection_data.host, t->connection_data.port);
607
	} else {
608
#ifdef GIT_CURL
609
		error = git_curl_stream_new(&t->io, t->connection_data.host, t->connection_data.port);
610
#else
611
		error = git_socket_stream_new(&t->io,  t->connection_data.host, t->connection_data.port);
612
#endif
613
	}
614

615 616
	if (error < 0)
		return error;
617

618
	GITERR_CHECK_VERSION(t->io, GIT_STREAM_VERSION, "git_stream");
619

620
	apply_proxy_config(t);
621

622
	error = git_stream_connect(t->io);
623

624 625
	if ((!error || error == GIT_ECERTIFICATE) && t->owner->certificate_check_cb != NULL &&
	    git_stream_is_encrypted(t->io)) {
626
		git_cert *cert;
627
		int is_valid = (error == GIT_OK);
628

629 630
		if ((error = git_stream_certificate(&cert, t->io)) < 0)
			return error;
631

632
		giterr_clear();
633
		error = t->owner->certificate_check_cb(cert, is_valid, t->connection_data.host, t->owner->message_cb_payload);
634

635 636 637 638 639 640
		if (error < 0) {
			if (!giterr_last())
				giterr_set(GITERR_NET, "user cancelled certificate check");

			return error;
		}
641
	}
642

643 644
	if (error < 0)
		return error;
645 646 647 648 649

	t->connected = 1;
	return 0;
}

650 651 652 653 654
static int http_stream_read(
	git_smart_subtransport_stream *stream,
	char *buffer,
	size_t buf_size,
	size_t *bytes_read)
655
{
656
	http_stream *s = (http_stream *)stream;
657
	http_subtransport *t = OWNING_SUBTRANSPORT(s);
658
	parser_context ctx;
659
	size_t bytes_parsed;
660

661
replay:
662
	*bytes_read = 0;
663

664
	assert(t->connected);
665

666
	if (!s->sent_request) {
667 668
		git_buf request = GIT_BUF_INIT;

669 670
		clear_parser_state(t);

671
		if (gen_request(&request, s, 0) < 0)
672
			return -1;
673

674
		if (git_stream_write(t->io, request.ptr, request.size, 0) < 0) {
675 676 677 678 679
			git_buf_free(&request);
			return -1;
		}

		git_buf_free(&request);
680

681
		s->sent_request = 1;
682 683
	}

684 685 686
	if (!s->received_response) {
		if (s->chunked) {
			assert(s->verb == post_verb);
687

688 689
			/* Flush, if necessary */
			if (s->chunk_buffer_len > 0 &&
690
				write_chunk(t->io, s->chunk_buffer, s->chunk_buffer_len) < 0)
691
				return -1;
692

693
			s->chunk_buffer_len = 0;
694

695
			/* Write the final chunk. */
696
			if (git_stream_write(t->io, "0\r\n\r\n", 5, 0) < 0)
697 698 699 700
				return -1;
		}

		s->received_response = 1;
701 702
	}

703
	while (!*bytes_read && !t->parse_finished) {
704
		size_t data_offset;
705
		int error;
706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721

		/*
		 * 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;
722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741

		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,
742 743
			t->parse_buffer.data + data_offset,
			t->parse_buffer.offset - data_offset);
744 745 746 747 748 749 750

		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;
751

752 753
			if ((error = http_connect(t)) < 0)
				return error;
754

755 756 757
			goto replay;
		}

758 759 760 761
		if (t->parse_error == PARSE_ERROR_EXT) {
			return t->error;
		}

762 763 764
		if (t->parse_error < 0)
			return -1;

765
		if (bytes_parsed != t->parse_buffer.offset - data_offset) {
766 767 768 769 770 771
			giterr_set(GITERR_NET,
				"HTTP parser error: %s",
				http_errno_description((enum http_errno)t->parser.http_errno));
			return -1;
		}
	}
772

773
	return 0;
774 775
}

776
static int http_stream_write_chunked(
777 778 779
	git_smart_subtransport_stream *stream,
	const char *buffer,
	size_t len)
780
{
781
	http_stream *s = (http_stream *)stream;
782
	http_subtransport *t = OWNING_SUBTRANSPORT(s);
783

784
	assert(t->connected);
785

786
	/* Send the request, if necessary */
787
	if (!s->sent_request) {
788 789
		git_buf request = GIT_BUF_INIT;

790 791
		clear_parser_state(t);

792
		if (gen_request(&request, s, 0) < 0)
793
			return -1;
794

795
		if (git_stream_write(t->io, request.ptr, request.size, 0) < 0) {
796 797 798
			git_buf_free(&request);
			return -1;
		}
799

800
		git_buf_free(&request);
801

802 803
		s->sent_request = 1;
	}
804

805 806 807
	if (len > CHUNK_SIZE) {
		/* Flush, if necessary */
		if (s->chunk_buffer_len > 0) {
808
			if (write_chunk(t->io, s->chunk_buffer, s->chunk_buffer_len) < 0)
809 810 811 812 813 814
				return -1;

			s->chunk_buffer_len = 0;
		}

		/* Write chunk directly */
815
		if (write_chunk(t->io, buffer, len) < 0)
816 817 818 819
			return -1;
	}
	else {
		/* Append as much to the buffer as we can */
820
		int count = min(CHUNK_SIZE - s->chunk_buffer_len, len);
821 822

		if (!s->chunk_buffer)
823
			s->chunk_buffer = git__malloc(CHUNK_SIZE);
824 825 826 827 828 829 830 831

		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) {
832
			if (write_chunk(t->io, s->chunk_buffer, s->chunk_buffer_len) < 0)
833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858
				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) {
859
		giterr_set(GITERR_NET, "subtransport configured for only one write");
860 861 862 863 864
		return -1;
	}

	clear_parser_state(t);

865
	if (gen_request(&request, s, len) < 0)
866 867
		return -1;

868
	if (git_stream_write(t->io, request.ptr, request.size, 0) < 0)
869 870
		goto on_error;

871
	if (len && git_stream_write(t->io, buffer, len, 0) < 0)
872 873 874 875 876
		goto on_error;

	git_buf_free(&request);
	s->sent_request = 1;

877 878 879 880 881 882
	return 0;

on_error:
	git_buf_free(&request);
	return -1;
}
883

884 885 886
static void http_stream_free(git_smart_subtransport_stream *stream)
{
	http_stream *s = (http_stream *)stream;
887

888 889 890
	if (s->chunk_buffer)
		git__free(s->chunk_buffer);

891 892 893
	if (s->redirect_url)
		git__free(s->redirect_url);

894 895
	git__free(s);
}
896

897 898
static int http_stream_alloc(http_subtransport *t,
	git_smart_subtransport_stream **stream)
899
{
900
	http_stream *s;
901

902 903
	if (!stream)
		return -1;
904

905
	s = git__calloc(sizeof(http_stream), 1);
906
	GITERR_CHECK_ALLOC(s);
907

908 909
	s->parent.subtransport = &t->parent;
	s->parent.read = http_stream_read;
910
	s->parent.write = http_stream_write_single;
911
	s->parent.free = http_stream_free;
912

913 914
	*stream = (git_smart_subtransport_stream *)s;
	return 0;
915 916
}

917 918 919
static int http_uploadpack_ls(
	http_subtransport *t,
	git_smart_subtransport_stream **stream)
920
{
921
	http_stream *s;
922

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

926
	s = (http_stream *)*stream;
927

928 929 930
	s->service = upload_pack_service;
	s->service_url = upload_pack_ls_service_url;
	s->verb = get_verb;
931

932
	return 0;
933 934
}

935 936 937
static int http_uploadpack(
	http_subtransport *t,
	git_smart_subtransport_stream **stream)
938
{
939
	http_stream *s;
940

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

944
	s = (http_stream *)*stream;
945

946 947 948
	s->service = upload_pack_service;
	s->service_url = upload_pack_service_url;
	s->verb = post_verb;
949

950
	return 0;
951 952
}

953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992
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;
}

993 994
static int http_action(
	git_smart_subtransport_stream **stream,
995
	git_smart_subtransport *subtransport,
996 997
	const char *url,
	git_smart_service_t action)
998
{
999
	http_subtransport *t = (http_subtransport *)subtransport;
1000
	int ret;
1001 1002 1003 1004

	if (!stream)
		return -1;

1005
	if ((!t->connection_data.host || !t->connection_data.port || !t->connection_data.path) &&
1006
		 (ret = gitno_connection_data_from_url(&t->connection_data, url, NULL)) < 0)
1007
		return ret;
1008

1009 1010
	if ((ret = http_connect(t)) < 0)
		return ret;
1011

1012 1013 1014
	switch (action) {
	case GIT_SERVICE_UPLOADPACK_LS:
		return http_uploadpack_ls(t, stream);
1015

1016 1017
	case GIT_SERVICE_UPLOADPACK:
		return http_uploadpack(t, stream);
1018

1019 1020
	case GIT_SERVICE_RECEIVEPACK_LS:
		return http_receivepack_ls(t, stream);
1021

1022 1023
	case GIT_SERVICE_RECEIVEPACK:
		return http_receivepack(t, stream);
1024
	}
1025

1026 1027 1028 1029
	*stream = NULL;
	return -1;
}

1030
static int http_close(git_smart_subtransport *subtransport)
1031
{
1032
	http_subtransport *t = (http_subtransport *) subtransport;
1033 1034
	git_http_auth_context *context;
	size_t i;
1035

1036 1037
	clear_parser_state(t);

1038 1039
	t->connected = 0;

1040 1041 1042 1043
	if (t->io) {
		git_stream_close(t->io);
		git_stream_free(t->io);
		t->io = NULL;
1044
	}
1045

1046 1047 1048 1049 1050
	if (t->cred) {
		t->cred->free(t->cred);
		t->cred = NULL;
	}

1051 1052 1053 1054 1055
	if (t->url_cred) {
		t->url_cred->free(t->url_cred);
		t->url_cred = NULL;
	}

1056 1057 1058 1059
	git_vector_foreach(&t->auth_contexts, i, context) {
		if (context->free)
			context->free(context);
	}
1060

1061
	git_vector_clear(&t->auth_contexts);
1062

1063
	gitno_connection_data_free_ptrs(&t->connection_data);
1064
	memset(&t->connection_data, 0x0, sizeof(gitno_connection_data));
1065

1066 1067 1068 1069 1070 1071 1072 1073 1074
	return 0;
}

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

	http_close(subtransport);

1075
	git_vector_free(&t->auth_contexts);
1076
	git__free(t);
1077 1078
}

1079
int git_smart_subtransport_http(git_smart_subtransport **out, git_transport *owner, void *param)
1080
{
1081
	http_subtransport *t;
1082

1083 1084
	GIT_UNUSED(param);

1085 1086
	if (!out)
		return -1;
1087

1088
	t = git__calloc(sizeof(http_subtransport), 1);
1089
	GITERR_CHECK_ALLOC(t);
1090

1091
	t->owner = (transport_smart *)owner;
1092
	t->parent.action = http_action;
1093
	t->parent.close = http_close;
1094
	t->parent.free = http_free;
1095

1096 1097 1098 1099 1100
	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;
1101

1102
	*out = (git_smart_subtransport *) t;
1103 1104
	return 0;
}
1105 1106

#endif /* !GIT_WINHTTP */