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

9 10 11
#include "git2.h"
#include "http_parser.h"
#include "buffer.h"
12
#include "netops.h"
13
#include "smart.h"
14 15 16 17 18 19

static const char *prefix_http = "http://";
static const char *prefix_https = "https://";
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";
20 21 22
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";
23 24
static const char *get_verb = "GET";
static const char *post_verb = "POST";
25
static const char *basic_authtype = "Basic";
26 27

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

29 30 31
#define PARSE_ERROR_GENERIC	-1
#define PARSE_ERROR_REPLAY	-2

32 33
#define CHUNK_SIZE	4096

34
enum last_cb {
35 36 37
	NONE,
	FIELD,
	VALUE
38
};
39

40 41 42 43
typedef enum {
	GIT_HTTP_AUTH_BASIC = 1,
} http_authmechanism_t;

44
typedef struct {
45 46 47
	git_smart_subtransport_stream parent;
	const char *service;
	const char *service_url;
48
	char *redirect_url;
49
	const char *verb;
50 51 52 53
	char *chunk_buffer;
	unsigned chunk_buffer_len;
	unsigned sent_request : 1,
		received_response : 1,
54 55
		chunked : 1,
		redirect_count : 3;
56 57 58 59
} http_stream;

typedef struct {
	git_smart_subtransport parent;
60
	transport_smart *owner;
61 62 63
	gitno_socket socket;
	const char *path;
	char *host;
64
	char *port;
65
	char *user_from_url;
66
	char *pass_from_url;
67
	git_cred *cred;
68
	git_cred *url_cred;
69
	http_authmechanism_t auth_mechanism;
70
	unsigned connected : 1,
71
		use_ssl : 1;
72 73

	/* Parser structures */
74
	http_parser parser;
75 76
	http_parser_settings settings;
	gitno_buffer parse_buffer;
77 78
	git_buf parse_header_name;
	git_buf parse_header_value;
79
	char parse_buffer_data[2048];
80
	char *content_type;
81
	char *location;
82
	git_vector www_authenticate;
83
	enum last_cb last_cb;
84 85
	int parse_error;
	unsigned parse_finished : 1;
86 87 88 89 90 91 92 93 94 95 96
} 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;
97

98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
static int apply_basic_credential(git_buf *buf, git_cred *cred)
{
	git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
	git_buf raw = GIT_BUF_INIT;
	int error = -1;

	git_buf_printf(&raw, "%s:%s", c->username, c->password);

	if (git_buf_oom(&raw) ||
		git_buf_puts(buf, "Authorization: Basic ") < 0 ||
		git_buf_put_base64(buf, git_buf_cstr(&raw), raw.size) < 0 ||
		git_buf_puts(buf, "\r\n") < 0)
		goto on_error;

	error = 0;

on_error:
	if (raw.size)
		memset(raw.ptr, 0x0, raw.size);

	git_buf_free(&raw);
	return error;
}

static int gen_request(
	git_buf *buf,
124 125
	http_stream *s,
	size_t content_length)
126
{
127 128 129 130
	http_subtransport *t = OWNING_SUBTRANSPORT(s);

	if (!t->path)
		t->path = "/";
131

132 133 134 135 136 137
	/* If we were redirected, make sure to respect that here */
	if (s->redirect_url)
		git_buf_printf(buf, "%s %s HTTP/1.1\r\n", s->verb, s->redirect_url);
	else
		git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, t->path, s->service_url);

138
	git_buf_puts(buf, "User-Agent: git/1.0 (libgit2 " LIBGIT2_VERSION ")\r\n");
139 140 141 142 143 144 145 146 147 148 149
	git_buf_printf(buf, "Host: %s\r\n", t->host);

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

	/* Apply credentials to the request */
153 154 155
	if (t->cred && t->cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT &&
		t->auth_mechanism == GIT_HTTP_AUTH_BASIC &&
		apply_basic_credential(buf, t->cred) < 0)
156 157
		return -1;

158 159 160 161 162 163 164 165
	/* Use url-parsed basic auth if username and password are both provided */
	if (!t->cred && t->user_from_url && t->pass_from_url) {
		if (!t->url_cred &&
			 git_cred_userpass_plaintext_new(&t->url_cred, t->user_from_url, t->pass_from_url) < 0)
			return -1;
		if (apply_basic_credential(buf, t->url_cred) < 0) return -1;
	}

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

168
	if (git_buf_oom(buf))
169
		return -1;
170 171

	return 0;
172 173
}

174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
static int parse_unauthorized_response(
	git_vector *www_authenticate,
	int *allowed_types,
	http_authmechanism_t *auth_mechanism)
{
	unsigned i;
	char *entry;

	git_vector_foreach(www_authenticate, i, entry) {
		if (!strncmp(entry, basic_authtype, 5) &&
			(entry[5] == '\0' || entry[5] == ' ')) {
			*allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT;
			*auth_mechanism = GIT_HTTP_AUTH_BASIC;
		}
	}

	return 0;
}

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

198 199 200 201 202
	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);
		}
203 204
	}
	else if (!strcmp("WWW-Authenticate", git_buf_cstr(name))) {
205
		char *dup = git__strdup(git_buf_cstr(value));
206
		GITERR_CHECK_ALLOC(dup);
207

208 209
		git_vector_insert(&t->www_authenticate, dup);
	}
210 211 212 213 214 215
	else if (!strcasecmp("Location", git_buf_cstr(name))) {
		if (!t->location) {
			t->location= git__strdup(git_buf_cstr(value));
			GITERR_CHECK_ALLOC(t->location);
		}
	}
216 217 218 219

	return 0;
}

220 221
static int on_header_field(http_parser *parser, const char *str, size_t len)
{
222 223
	parser_context *ctx = (parser_context *) parser->data;
	http_subtransport *t = ctx->t;
224

225 226 227 228 229
	/* 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;
230

231 232
	if (NONE == t->last_cb || VALUE == t->last_cb)
		git_buf_clear(&t->parse_header_name);
233

234 235
	if (git_buf_put(&t->parse_header_name, str, len) < 0)
		return t->parse_error = PARSE_ERROR_GENERIC;
236

237
	t->last_cb = FIELD;
238
	return 0;
239 240 241 242
}

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

246
	assert(NONE != t->last_cb);
247

248 249
	if (FIELD == t->last_cb)
		git_buf_clear(&t->parse_header_value);
250

251 252
	if (git_buf_put(&t->parse_header_value, str, len) < 0)
		return t->parse_error = PARSE_ERROR_GENERIC;
253

254
	t->last_cb = VALUE;
255
	return 0;
256 257 258 259
}

static int on_headers_complete(http_parser *parser)
{
260 261
	parser_context *ctx = (parser_context *) parser->data;
	http_subtransport *t = ctx->t;
262 263
	http_stream *s = ctx->s;
	git_buf buf = GIT_BUF_INIT;
264

265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
	/* 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;

	/* Check for an authentication failure. */
	if (parser->status_code == 401 &&
		get_verb == s->verb &&
		t->owner->cred_acquire_cb) {
		int allowed_types = 0;

		if (parse_unauthorized_response(&t->www_authenticate,
			&allowed_types, &t->auth_mechanism) < 0)
			return t->parse_error = PARSE_ERROR_GENERIC;

		if (allowed_types &&
			(!t->cred || 0 == (t->cred->credtype & allowed_types))) {

			if (t->owner->cred_acquire_cb(&t->cred,
					t->owner->url,
286
					t->user_from_url,
287 288
					allowed_types,
					t->owner->cred_acquire_payload) < 0)
289 290 291 292 293 294 295
				return PARSE_ERROR_GENERIC;

			assert(t->cred);

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

298
	/* Check for a redirect.
299
	 * Right now we only permit a redirect to the same hostname. */
300 301 302 303
	if ((parser->status_code == 301 ||
		 parser->status_code == 302 ||
		 (parser->status_code == 303 && get_verb == s->verb) ||
		 parser->status_code == 307) &&
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
		t->location) {

		if (s->redirect_count >= 7) {
			giterr_set(GITERR_NET, "Too many redirects");
			return t->parse_error = PARSE_ERROR_GENERIC;
		}

		if (t->location[0] != '/') {
			giterr_set(GITERR_NET, "Only relative redirects are supported");
			return t->parse_error = PARSE_ERROR_GENERIC;
		}

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

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

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

		return t->parse_error = PARSE_ERROR_REPLAY;
	}

330 331 332 333 334 335
	/* Check for a 200 HTTP status code. */
	if (parser->status_code != 200) {
		giterr_set(GITERR_NET,
			"Unexpected HTTP status code: %d",
			parser->status_code);
		return t->parse_error = PARSE_ERROR_GENERIC;
336 337
	}

338 339 340 341 342
	/* The response must contain a Content-Type header. */
	if (!t->content_type) {
		giterr_set(GITERR_NET, "No Content-Type header in response");
		return t->parse_error = PARSE_ERROR_GENERIC;
	}
343

344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
	/* 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,
			"Invalid Content-Type: %s",
			t->content_type);
		return t->parse_error = PARSE_ERROR_GENERIC;
363
	}
364

365 366
	git_buf_free(&buf);

367 368 369 370 371
	return 0;
}

static int on_message_complete(http_parser *parser)
{
372 373
	parser_context *ctx = (parser_context *) parser->data;
	http_subtransport *t = ctx->t;
374

375
	t->parse_finished = 1;
376

377 378 379
	return 0;
}

380
static int on_body_fill_buffer(http_parser *parser, const char *str, size_t len)
381
{
382 383
	parser_context *ctx = (parser_context *) parser->data;
	http_subtransport *t = ctx->t;
384

385
	if (ctx->buf_size < len) {
386
		giterr_set(GITERR_NET, "Can't fit data in the buffer");
387
		return t->parse_error = PARSE_ERROR_GENERIC;
388
	}
389

390 391 392 393
	memcpy(ctx->buffer, str, len);
	*(ctx->bytes_read) += len;
	ctx->buffer += len;
	ctx->buf_size -= len;
394

395 396
	return 0;
}
397

398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
static void clear_parser_state(http_subtransport *t)
{
	unsigned i;
	char *entry;

	http_parser_init(&t->parser, HTTP_RESPONSE);
	gitno_buffer_setup(&t->socket,
		&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;

422 423 424
	git__free(t->location);
	t->location = NULL;

425 426 427 428 429 430
	git_vector_foreach(&t->www_authenticate, i, entry)
		git__free(entry);

	git_vector_free(&t->www_authenticate);
}

431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
static int write_chunk(gitno_socket *socket, const char *buffer, size_t len)
{
	git_buf buf = GIT_BUF_INIT;

	/* Chunk header */
	git_buf_printf(&buf, "%X\r\n", (unsigned)len);

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

	if (gitno_send(socket, buf.ptr, buf.size, 0) < 0) {
		git_buf_free(&buf);
		return -1;
	}

	git_buf_free(&buf);

	/* Chunk body */
	if (len > 0 && gitno_send(socket, buffer, len, 0) < 0)
		return -1;

	/* Chunk footer */
	if (gitno_send(socket, "\r\n", 2, 0) < 0)
		return -1;

	return 0;
}

459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
static int http_connect(http_subtransport *t)
{
	int flags = 0;

	if (t->connected &&
		http_should_keep_alive(&t->parser) &&
		http_body_is_final(&t->parser))
		return 0;

	if (t->socket.socket)
		gitno_close(&t->socket);

	if (t->use_ssl) {
		int tflags;

		if (t->owner->parent.read_flags(&t->owner->parent, &tflags) < 0)
			return -1;

		flags |= GITNO_CONNECT_SSL;

		if (GIT_TRANSPORTFLAGS_NO_CHECK_CERT & tflags)
			flags |= GITNO_CONNECT_SSL_NO_CHECK_CERT;
	}

	if (gitno_connect(&t->socket, t->host, t->port, flags) < 0)
		return -1;

	t->connected = 1;
	return 0;
}

490 491 492 493 494
static int http_stream_read(
	git_smart_subtransport_stream *stream,
	char *buffer,
	size_t buf_size,
	size_t *bytes_read)
495
{
496
	http_stream *s = (http_stream *)stream;
497
	http_subtransport *t = OWNING_SUBTRANSPORT(s);
498
	parser_context ctx;
499
	size_t bytes_parsed;
500

501
replay:
502
	*bytes_read = 0;
503

504
	assert(t->connected);
505

506
	if (!s->sent_request) {
507 508
		git_buf request = GIT_BUF_INIT;

509 510
		clear_parser_state(t);

511
		if (gen_request(&request, s, 0) < 0) {
512
			giterr_set(GITERR_NET, "Failed to generate request");
513 514
			return -1;
		}
515

516 517 518 519 520 521
		if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) {
			git_buf_free(&request);
			return -1;
		}

		git_buf_free(&request);
522

523
		s->sent_request = 1;
524 525
	}

526 527 528
	if (!s->received_response) {
		if (s->chunked) {
			assert(s->verb == post_verb);
529

530 531 532 533
			/* Flush, if necessary */
			if (s->chunk_buffer_len > 0 &&
				write_chunk(&t->socket, s->chunk_buffer, s->chunk_buffer_len) < 0)
				return -1;
534

535
			s->chunk_buffer_len = 0;
536

537 538 539 540 541 542
			/* Write the final chunk. */
			if (gitno_send(&t->socket, "0\r\n\r\n", 5, 0) < 0)
				return -1;
		}

		s->received_response = 1;
543 544
	}

545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
	while (!*bytes_read && !t->parse_finished) {
		t->parse_buffer.offset = 0;

		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,
			t->parse_buffer.data,
			t->parse_buffer.offset);

		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;
576 577 578 579

			if (http_connect(t) < 0)
				return -1;

580 581 582 583 584 585 586 587 588 589 590 591 592
			goto replay;
		}

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

		if (bytes_parsed != t->parse_buffer.offset) {
			giterr_set(GITERR_NET,
				"HTTP parser error: %s",
				http_errno_description((enum http_errno)t->parser.http_errno));
			return -1;
		}
	}
593

594
	return 0;
595 596
}

597
static int http_stream_write_chunked(
598 599 600
	git_smart_subtransport_stream *stream,
	const char *buffer,
	size_t len)
601
{
602
	http_stream *s = (http_stream *)stream;
603
	http_subtransport *t = OWNING_SUBTRANSPORT(s);
604

605
	assert(t->connected);
606

607
	/* Send the request, if necessary */
608
	if (!s->sent_request) {
609 610
		git_buf request = GIT_BUF_INIT;

611 612
		clear_parser_state(t);

613
		if (gen_request(&request, s, 0) < 0) {
614 615 616
			giterr_set(GITERR_NET, "Failed to generate request");
			return -1;
		}
617

618 619 620 621
		if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) {
			git_buf_free(&request);
			return -1;
		}
622

623
		git_buf_free(&request);
624

625 626
		s->sent_request = 1;
	}
627

628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
	if (len > CHUNK_SIZE) {
		/* Flush, if necessary */
		if (s->chunk_buffer_len > 0) {
			if (write_chunk(&t->socket, s->chunk_buffer, s->chunk_buffer_len) < 0)
				return -1;

			s->chunk_buffer_len = 0;
		}

		/* Write chunk directly */
		if (write_chunk(&t->socket, buffer, len) < 0)
			return -1;
	}
	else {
		/* Append as much to the buffer as we can */
643
		int count = min(CHUNK_SIZE - s->chunk_buffer_len, len);
644 645

		if (!s->chunk_buffer)
646
			s->chunk_buffer = git__malloc(CHUNK_SIZE);
647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701

		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) {
			if (write_chunk(&t->socket, s->chunk_buffer, s->chunk_buffer_len) < 0)
				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) {
		giterr_set(GITERR_NET, "Subtransport configured for only one write");
		return -1;
	}

	clear_parser_state(t);

	if (gen_request(&request, s, len) < 0) {
		giterr_set(GITERR_NET, "Failed to generate request");
		return -1;
	}

	if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0)
		goto on_error;

	if (len && gitno_send(&t->socket, buffer, len, 0) < 0)
		goto on_error;

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

702 703 704 705 706 707
	return 0;

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

709 710 711
static void http_stream_free(git_smart_subtransport_stream *stream)
{
	http_stream *s = (http_stream *)stream;
712

713 714 715
	if (s->chunk_buffer)
		git__free(s->chunk_buffer);

716 717 718
	if (s->redirect_url)
		git__free(s->redirect_url);

719 720
	git__free(s);
}
721

722 723
static int http_stream_alloc(http_subtransport *t,
	git_smart_subtransport_stream **stream)
724
{
725
	http_stream *s;
726

727 728
	if (!stream)
		return -1;
729

730
	s = git__calloc(sizeof(http_stream), 1);
731
	GITERR_CHECK_ALLOC(s);
732

733 734
	s->parent.subtransport = &t->parent;
	s->parent.read = http_stream_read;
735
	s->parent.write = http_stream_write_single;
736
	s->parent.free = http_stream_free;
737

738 739
	*stream = (git_smart_subtransport_stream *)s;
	return 0;
740 741
}

742 743 744
static int http_uploadpack_ls(
	http_subtransport *t,
	git_smart_subtransport_stream **stream)
745
{
746
	http_stream *s;
747

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

751
	s = (http_stream *)*stream;
752

753 754 755
	s->service = upload_pack_service;
	s->service_url = upload_pack_ls_service_url;
	s->verb = get_verb;
756

757
	return 0;
758 759
}

760 761 762
static int http_uploadpack(
	http_subtransport *t,
	git_smart_subtransport_stream **stream)
763
{
764
	http_stream *s;
765

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

769
	s = (http_stream *)*stream;
770

771 772 773
	s->service = upload_pack_service;
	s->service_url = upload_pack_service_url;
	s->verb = post_verb;
774

775
	return 0;
776 777
}

778 779 780 781 782 783 784 785 786 787 788 789 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
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;
}

818 819
static int http_action(
	git_smart_subtransport_stream **stream,
820
	git_smart_subtransport *subtransport,
821 822
	const char *url,
	git_smart_service_t action)
823
{
824 825
	http_subtransport *t = (http_subtransport *)subtransport;
	const char *default_port = NULL;
826
	int ret;
827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842

	if (!stream)
		return -1;

	if (!t->host || !t->port || !t->path) {
		if (!git__prefixcmp(url, prefix_http)) {
			url = url + strlen(prefix_http);
			default_port = "80";
		}

		if (!git__prefixcmp(url, prefix_https)) {
			url += strlen(prefix_https);
			default_port = "443";
			t->use_ssl = 1;
		}

843 844 845
		if (!default_port)
			return -1;

846 847
		if ((ret = gitno_extract_url_parts(&t->host, &t->port,
						&t->user_from_url, &t->pass_from_url, url, default_port)) < 0)
848 849 850
			return ret;

		t->path = strchr(url, '/');
851
	}
852

853 854
	if (http_connect(t) < 0)
		return -1;
855

856 857 858 859 860 861 862
	switch (action)
	{
		case GIT_SERVICE_UPLOADPACK_LS:
			return http_uploadpack_ls(t, stream);

		case GIT_SERVICE_UPLOADPACK:
			return http_uploadpack(t, stream);
863 864 865 866 867 868

		case GIT_SERVICE_RECEIVEPACK_LS:
			return http_receivepack_ls(t, stream);

		case GIT_SERVICE_RECEIVEPACK:
			return http_receivepack(t, stream);
869
	}
870

871 872 873 874
	*stream = NULL;
	return -1;
}

875
static int http_close(git_smart_subtransport *subtransport)
876
{
877
	http_subtransport *t = (http_subtransport *) subtransport;
878

879 880
	clear_parser_state(t);

881
	if (t->socket.socket) {
882
		gitno_close(&t->socket);
883 884
		memset(&t->socket, 0x0, sizeof(gitno_socket));
	}
885

886 887 888 889 890
	if (t->cred) {
		t->cred->free(t->cred);
		t->cred = NULL;
	}

891 892 893 894 895
	if (t->url_cred) {
		t->url_cred->free(t->url_cred);
		t->url_cred = NULL;
	}

896 897 898 899 900 901 902 903 904 905
	if (t->host) {
		git__free(t->host);
		t->host = NULL;
	}

	if (t->port) {
		git__free(t->port);
		t->port = NULL;
	}

906 907 908 909 910 911 912 913 914 915
	if (t->user_from_url) {
		git__free(t->user_from_url);
		t->user_from_url = NULL;
	}

	if (t->pass_from_url) {
		git__free(t->pass_from_url);
		t->pass_from_url = NULL;
	}

916 917 918 919 920 921 922 923 924
	return 0;
}

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

	http_close(subtransport);

925
	git__free(t);
926 927
}

Vicent Marti committed
928
int git_smart_subtransport_http(git_smart_subtransport **out, git_transport *owner)
929
{
930
	http_subtransport *t;
931

932 933
	if (!out)
		return -1;
934

935
	t = git__calloc(sizeof(http_subtransport), 1);
936
	GITERR_CHECK_ALLOC(t);
937

938
	t->owner = (transport_smart *)owner;
939
	t->parent.action = http_action;
940
	t->parent.close = http_close;
941
	t->parent.free = http_free;
942

943 944 945 946 947
	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;
948

949
	*out = (git_smart_subtransport *) t;
950 951
	return 0;
}
952 953

#endif /* !GIT_WINHTTP */