http.c 24.9 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 24
#include "streams/tls.h"
#include "streams/socket.h"
#include "streams/curl.h"
25

26 27 28 29
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 },
};
30

31 32 33
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";
34 35 36
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";
37 38 39 40
static const char *get_verb = "GET";
static const char *post_verb = "POST";

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

42 43
#define PARSE_ERROR_GENERIC	-1
#define PARSE_ERROR_REPLAY	-2
44 45
/** Look at the user field */
#define PARSE_ERROR_EXT         -3
46

47 48
#define CHUNK_SIZE	4096

49
enum last_cb {
50 51 52
	NONE,
	FIELD,
	VALUE
53
};
54

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

typedef struct {
	git_smart_subtransport parent;
71
	transport_smart *owner;
72
	git_stream *io;
73 74
	gitno_connection_data connection_data;
	bool connected;
75 76

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

91 92 93 94
	/* Authentication */
	git_cred *cred;
	git_cred *url_cred;
	git_vector auth_contexts;
95 96 97 98 99 100 101 102 103 104 105
} 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;
106

107
static bool credtype_match(git_http_auth_scheme *scheme, void *data)
108
{
109
	unsigned int credtype = *(unsigned int *)data;
110

111
	return !!(scheme->credtypes & credtype);
112 113
}

114
static bool challenge_match(git_http_auth_scheme *scheme, void *data)
115
{
116 117 118 119 120
	const char *scheme_name = scheme->name;
	const char *challenge = (const char *)data;
	size_t scheme_len;

	scheme_len = strlen(scheme_name);
121
	return (strncasecmp(challenge, scheme_name, scheme_len) == 0 &&
122
		(challenge[scheme_len] == '\0' || challenge[scheme_len] == ' '));
123 124
}

125 126 127 128 129
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)
130
{
131 132
	git_http_auth_scheme *scheme = NULL;
	git_http_auth_context *context = NULL, *c;
133 134
	size_t i;

135
	*out = NULL;
136

137 138 139 140
	for (i = 0; i < ARRAY_SIZE(auth_schemes); i++) {
		if (scheme_match(&auth_schemes[i], data)) {
			scheme = &auth_schemes[i];
			break;
141 142 143
		}
	}

144 145
	if (!scheme)
		return 0;
146

147 148 149 150 151 152
	/* 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;
		}
153 154
	}

155 156 157 158 159 160 161 162
	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;
	}
163

164
	*out = context;
165 166 167 168

	return 0;
}

169
static int apply_credentials(git_buf *buf, http_subtransport *t)
170
{
171 172 173 174 175 176 177 178 179
	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;
180

181
		cred = t->url_cred;
182 183
	}

184 185
	if (!cred)
		return 0;
186

187 188
	/* Get or create a context for the best scheme for this cred type */
	if (auth_context_match(&context, t, credtype_match, &cred->credtype) < 0)
189 190
		return -1;

191
	return context->next_token(buf, context, cred);
192 193
}

194 195
static int gen_request(
	git_buf *buf,
196 197
	http_stream *s,
	size_t content_length)
198
{
199
	http_subtransport *t = OWNING_SUBTRANSPORT(s);
200
	const char *path = t->connection_data.path ? t->connection_data.path : "/";
201
	size_t i;
202

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

205 206 207
	git_buf_puts(buf, "User-Agent: ");
	git_http__user_agent(buf);
	git_buf_puts(buf, "\r\n");
208
	git_buf_printf(buf, "Host: %s\r\n", t->connection_data.host);
209 210 211 212 213 214 215 216 217 218

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

221 222 223
	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]);
224 225
	}

226
	/* Apply credentials to the request */
227 228
	if (apply_credentials(buf, t) < 0)
		return -1;
229

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

232
	if (git_buf_oom(buf))
233
		return -1;
234 235

	return 0;
236 237
}

238
static int parse_authenticate_response(
239
	git_vector *www_authenticate,
240 241
	http_subtransport *t,
	int *allowed_types)
242
{
243 244 245
	git_http_auth_context *context;
	char *challenge;
	size_t i;
246

247 248 249 250 251
	git_vector_foreach(www_authenticate, i, challenge) {
		if (auth_context_match(&context, t, challenge_match, challenge) < 0)
			return -1;
		else if (!context)
			continue;
252

253 254 255
		if (context->set_challenge &&
			context->set_challenge(context, challenge) < 0)
			return -1;
256

257
		*allowed_types |= context->credtypes;
258 259 260 261 262 263 264 265 266 267
	}

	return 0;
}

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

268 269 270 271 272
	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);
		}
273
	}
274
	else if (!strcasecmp("WWW-Authenticate", git_buf_cstr(name))) {
275
		char *dup = git__strdup(git_buf_cstr(value));
276
		GITERR_CHECK_ALLOC(dup);
277

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

	return 0;
}

290 291
static int on_header_field(http_parser *parser, const char *str, size_t len)
{
292 293
	parser_context *ctx = (parser_context *) parser->data;
	http_subtransport *t = ctx->t;
294

295 296 297 298 299
	/* 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;
300

301 302
	if (NONE == t->last_cb || VALUE == t->last_cb)
		git_buf_clear(&t->parse_header_name);
303

304 305
	if (git_buf_put(&t->parse_header_name, str, len) < 0)
		return t->parse_error = PARSE_ERROR_GENERIC;
306

307
	t->last_cb = FIELD;
308
	return 0;
309 310 311 312
}

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

316
	assert(NONE != t->last_cb);
317

318 319
	if (FIELD == t->last_cb)
		git_buf_clear(&t->parse_header_value);
320

321 322
	if (git_buf_put(&t->parse_header_value, str, len) < 0)
		return t->parse_error = PARSE_ERROR_GENERIC;
323

324
	t->last_cb = VALUE;
325
	return 0;
326 327 328 329
}

static int on_headers_complete(http_parser *parser)
{
330 331
	parser_context *ctx = (parser_context *) parser->data;
	http_subtransport *t = ctx->t;
332 333
	http_stream *s = ctx->s;
	git_buf buf = GIT_BUF_INIT;
334
	int error = 0, no_callback = 0, allowed_auth_types = 0;
335

336 337 338 339 340 341
	/* 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;

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

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

				error = t->owner->cred_acquire_cb(&t->cred,
								  t->owner->url,
								  t->connection_data.user,
364
								  allowed_auth_types,
365 366 367 368 369
								  t->owner->cred_acquire_payload);

				if (error == GIT_PASSTHROUGH) {
					no_callback = 1;
				} else if (error < 0) {
370 371
					t->error = error;
					return t->parse_error = PARSE_ERROR_EXT;
372 373 374
				} else {
					assert(t->cred);

375 376 377 378 379
					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;
					}

380
					/* Successfully acquired a credential. */
Edward Thomson committed
381 382
					t->parse_error = PARSE_ERROR_REPLAY;
					return 0;
383 384 385
				}
			}
		}
386

387 388
		if (no_callback) {
			giterr_set(GITERR_NET, "authentication required but no callback set");
389 390
			return t->parse_error = PARSE_ERROR_GENERIC;
		}
391 392
	}

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

		if (s->redirect_count >= 7) {
402
			giterr_set(GITERR_NET, "too many redirects");
403 404 405
			return t->parse_error = PARSE_ERROR_GENERIC;
		}

406
		if (gitno_connection_data_from_url(&t->connection_data, t->location, s->service_url) < 0)
407
			return t->parse_error = PARSE_ERROR_GENERIC;
408 409 410 411 412 413 414 415 416 417 418 419

		/* 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
420 421
		t->parse_error = PARSE_ERROR_REPLAY;
		return 0;
422 423
	}

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

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

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

459
	git_buf_dispose(&buf);
460

461 462 463 464 465
	return 0;
}

static int on_message_complete(http_parser *parser)
{
466 467
	parser_context *ctx = (parser_context *) parser->data;
	http_subtransport *t = ctx->t;
468

469
	t->parse_finished = 1;
470

471 472 473
	return 0;
}

474
static int on_body_fill_buffer(http_parser *parser, const char *str, size_t len)
475
{
476 477
	parser_context *ctx = (parser_context *) parser->data;
	http_subtransport *t = ctx->t;
478

Edward Thomson committed
479 480 481 482 483 484 485
	/* 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;

486
	if (ctx->buf_size < len) {
487
		giterr_set(GITERR_NET, "can't fit data in the buffer");
488
		return t->parse_error = PARSE_ERROR_GENERIC;
489
	}
490

491 492 493 494
	memcpy(ctx->buffer, str, len);
	*(ctx->bytes_read) += len;
	ctx->buffer += len;
	ctx->buf_size -= len;
495

496 497
	return 0;
}
498

499 500 501
static void clear_parser_state(http_subtransport *t)
{
	http_parser_init(&t->parser, HTTP_RESPONSE);
502
	gitno_buffer_setup_fromstream(t->io,
503 504 505 506 507 508 509 510
		&t->parse_buffer,
		t->parse_buffer_data,
		sizeof(t->parse_buffer_data));

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

511
	git_buf_dispose(&t->parse_header_name);
512 513
	git_buf_init(&t->parse_header_name, 0);

514
	git_buf_dispose(&t->parse_header_value);
515 516 517 518 519
	git_buf_init(&t->parse_header_value, 0);

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

520 521 522
	git__free(t->location);
	t->location = NULL;

523
	git_vector_free_deep(&t->www_authenticate);
524 525
}

526
static int write_chunk(git_stream *io, const char *buffer, size_t len)
527 528 529 530
{
	git_buf buf = GIT_BUF_INIT;

	/* Chunk header */
531
	git_buf_printf(&buf, "%" PRIxZ "\r\n", len);
532 533 534 535

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

536
	if (git_stream_write(io, buf.ptr, buf.size, 0) < 0) {
537
		git_buf_dispose(&buf);
538 539 540
		return -1;
	}

541
	git_buf_dispose(&buf);
542 543

	/* Chunk body */
544
	if (len > 0 && git_stream_write(io, buffer, len, 0) < 0)
545 546 547
		return -1;

	/* Chunk footer */
548
	if (git_stream_write(io, "\r\n", 2, 0) < 0)
549 550 551 552 553
		return -1;

	return 0;
}

554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
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;

574 575 576
		opts.credentials = t->owner->proxy.credentials;
		opts.certificate_check = t->owner->proxy.certificate_check;
		opts.payload = t->owner->proxy.payload;
577
		opts.type = GIT_PROXY_SPECIFIED;
578 579 580 581 582 583 584 585 586 587
		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);
}

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

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

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

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

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

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

619
	apply_proxy_config(t);
620

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

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

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

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

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

			return error;
		}
640
	}
641

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

	t->connected = 1;
	return 0;
}

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

660
replay:
661
	*bytes_read = 0;
662

663
	assert(t->connected);
664

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

668 669
		clear_parser_state(t);

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

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

678
		git_buf_dispose(&request);
679

680
		s->sent_request = 1;
681 682
	}

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

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

692
			s->chunk_buffer_len = 0;
693

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

		s->received_response = 1;
700 701
	}

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

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

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

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

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

754 755 756
			goto replay;
		}

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

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

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

772
	return 0;
773 774
}

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

783
	assert(t->connected);
784

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

789 790
		clear_parser_state(t);

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

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

799
		git_buf_dispose(&request);
800

801 802
		s->sent_request = 1;
	}
803

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

			s->chunk_buffer_len = 0;
		}

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

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

		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) {
831
			if (write_chunk(t->io, s->chunk_buffer, s->chunk_buffer_len) < 0)
832 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
				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) {
858
		giterr_set(GITERR_NET, "subtransport configured for only one write");
859 860 861 862 863
		return -1;
	}

	clear_parser_state(t);

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

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

870
	if (len && git_stream_write(t->io, buffer, len, 0) < 0)
871 872
		goto on_error;

873
	git_buf_dispose(&request);
874 875
	s->sent_request = 1;

876 877 878
	return 0;

on_error:
879
	git_buf_dispose(&request);
880 881
	return -1;
}
882

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

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

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

893 894
	git__free(s);
}
895

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

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

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

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

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

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

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

925
	s = (http_stream *)*stream;
926

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

931
	return 0;
932 933
}

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

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

943
	s = (http_stream *)*stream;
944

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

949
	return 0;
950 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
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;
}

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

	if (!stream)
		return -1;

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

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

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

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

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

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

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

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

1035 1036
	clear_parser_state(t);

1037 1038
	t->connected = 0;

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

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

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

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

1060
	git_vector_clear(&t->auth_contexts);
1061

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

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

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

	http_close(subtransport);

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

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

1082 1083
	GIT_UNUSED(param);

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

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

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

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

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

#endif /* !GIT_WINHTTP */