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

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";
18 19 20
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";
21 22
static const char *get_verb = "GET";
static const char *post_verb = "POST";
23
static const char *basic_authtype = "Basic";
24 25

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

27 28 29
#define PARSE_ERROR_GENERIC	-1
#define PARSE_ERROR_REPLAY	-2

30 31
#define CHUNK_SIZE	4096

32
enum last_cb {
33 34 35
	NONE,
	FIELD,
	VALUE
36
};
37

38 39 40 41
typedef enum {
	GIT_HTTP_AUTH_BASIC = 1,
} http_authmechanism_t;

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

typedef struct {
	git_smart_subtransport parent;
58
	transport_smart *owner;
59
	gitno_socket socket;
60
	gitno_connection_data connection_data;
61
	git_cred *cred;
62
	git_cred *url_cred;
63
	http_authmechanism_t auth_mechanism;
64
	bool connected;
65 66

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

91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
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,
117 118
	http_stream *s,
	size_t content_length)
119
{
120
	http_subtransport *t = OWNING_SUBTRANSPORT(s);
121
	const char *path = t->connection_data.path ? t->connection_data.path : "/";
122

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

125
	git_buf_puts(buf, "User-Agent: git/1.0 (libgit2 " LIBGIT2_VERSION ")\r\n");
126
	git_buf_printf(buf, "Host: %s\r\n", t->connection_data.host);
127 128 129 130 131 132 133 134 135 136

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

	/* Apply credentials to the request */
140 141 142
	if (t->cred && t->cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT &&
		t->auth_mechanism == GIT_HTTP_AUTH_BASIC &&
		apply_basic_credential(buf, t->cred) < 0)
143 144
		return -1;

145
	/* Use url-parsed basic auth if username and password are both provided */
146 147 148
	if (!t->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)
149 150 151 152
			return -1;
		if (apply_basic_credential(buf, t->url_cred) < 0) return -1;
	}

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

155
	if (git_buf_oom(buf))
156
		return -1;
157 158

	return 0;
159 160
}

161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
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;

185 186 187 188 189
	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);
		}
190 191
	}
	else if (!strcmp("WWW-Authenticate", git_buf_cstr(name))) {
192
		char *dup = git__strdup(git_buf_cstr(value));
193
		GITERR_CHECK_ALLOC(dup);
194

195 196
		git_vector_insert(&t->www_authenticate, dup);
	}
197 198
	else if (!strcasecmp("Location", git_buf_cstr(name))) {
		if (!t->location) {
199
			t->location = git__strdup(git_buf_cstr(value));
200 201 202
			GITERR_CHECK_ALLOC(t->location);
		}
	}
203 204 205 206

	return 0;
}

207 208
static int on_header_field(http_parser *parser, const char *str, size_t len)
{
209 210
	parser_context *ctx = (parser_context *) parser->data;
	http_subtransport *t = ctx->t;
211

212 213 214 215 216
	/* 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;
217

218 219
	if (NONE == t->last_cb || VALUE == t->last_cb)
		git_buf_clear(&t->parse_header_name);
220

221 222
	if (git_buf_put(&t->parse_header_name, str, len) < 0)
		return t->parse_error = PARSE_ERROR_GENERIC;
223

224
	t->last_cb = FIELD;
225
	return 0;
226 227 228 229
}

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

233
	assert(NONE != t->last_cb);
234

235 236
	if (FIELD == t->last_cb)
		git_buf_clear(&t->parse_header_value);
237

238 239
	if (git_buf_put(&t->parse_header_value, str, len) < 0)
		return t->parse_error = PARSE_ERROR_GENERIC;
240

241
	t->last_cb = VALUE;
242
	return 0;
243 244 245 246
}

static int on_headers_complete(http_parser *parser)
{
247 248
	parser_context *ctx = (parser_context *) parser->data;
	http_subtransport *t = ctx->t;
249 250
	http_stream *s = ctx->s;
	git_buf buf = GIT_BUF_INIT;
251
	int error = 0, no_callback = 0;
252

253 254 255 256 257 258 259
	/* 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. */
260

261
	if (parser->status_code == 401 &&
262
	    get_verb == s->verb) {
263
		if (!t->owner->cred_acquire_cb) {
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
			no_callback = 1;
		} else {
			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))) {

				error = t->owner->cred_acquire_cb(&t->cred,
								  t->owner->url,
								  t->connection_data.user,
								  allowed_types,
								  t->owner->cred_acquire_payload);

				if (error == GIT_PASSTHROUGH) {
					no_callback = 1;
				} else if (error < 0) {
					return PARSE_ERROR_GENERIC;
				} else {
					assert(t->cred);

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

294 295
		if (no_callback) {
			giterr_set(GITERR_NET, "authentication required but no callback set");
296 297
			return t->parse_error = PARSE_ERROR_GENERIC;
		}
298 299
	}

300
	/* Check for a redirect.
301
	 * Right now we only permit a redirect to the same hostname. */
302
	if ((parser->status_code == 301 ||
303 304 305 306
	     parser->status_code == 302 ||
	     (parser->status_code == 303 && get_verb == s->verb) ||
	     parser->status_code == 307) &&
	    t->location) {
307 308 309 310 311 312

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

313
		if (gitno_connection_data_from_url(&t->connection_data, t->location, s->service_url) < 0)
314
			return t->parse_error = PARSE_ERROR_GENERIC;
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329

		/* 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
static void clear_parser_state(http_subtransport *t)
{
	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;

419 420 421
	git__free(t->location);
	t->location = NULL;

422
	git_vector_free_deep(&t->www_authenticate);
423 424
}

425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
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;
}

453 454 455 456 457 458 459 460 461 462 463 464
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);

465
	if (t->connection_data.use_ssl) {
466 467 468 469 470 471 472 473 474 475 476
		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;
	}

477
	if (gitno_connect(&t->socket, t->connection_data.host, t->connection_data.port, flags) < 0)
478 479 480 481 482 483
		return -1;

	t->connected = 1;
	return 0;
}

484 485 486 487 488
static int http_stream_read(
	git_smart_subtransport_stream *stream,
	char *buffer,
	size_t buf_size,
	size_t *bytes_read)
489
{
490
	http_stream *s = (http_stream *)stream;
491
	http_subtransport *t = OWNING_SUBTRANSPORT(s);
492
	parser_context ctx;
493
	size_t bytes_parsed;
494

495
replay:
496
	*bytes_read = 0;
497

498
	assert(t->connected);
499

500
	if (!s->sent_request) {
501 502
		git_buf request = GIT_BUF_INIT;

503 504
		clear_parser_state(t);

505
		if (gen_request(&request, s, 0) < 0) {
506
			giterr_set(GITERR_NET, "Failed to generate request");
507 508
			return -1;
		}
509

510 511 512 513 514 515
		if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) {
			git_buf_free(&request);
			return -1;
		}

		git_buf_free(&request);
516

517
		s->sent_request = 1;
518 519
	}

520 521 522
	if (!s->received_response) {
		if (s->chunked) {
			assert(s->verb == post_verb);
523

524 525 526 527
			/* Flush, if necessary */
			if (s->chunk_buffer_len > 0 &&
				write_chunk(&t->socket, s->chunk_buffer, s->chunk_buffer_len) < 0)
				return -1;
528

529
			s->chunk_buffer_len = 0;
530

531 532 533 534 535 536
			/* Write the final chunk. */
			if (gitno_send(&t->socket, "0\r\n\r\n", 5, 0) < 0)
				return -1;
		}

		s->received_response = 1;
537 538
	}

539 540 541 542 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
	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;
570 571 572 573

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

574 575 576 577 578 579 580 581 582 583 584 585 586
			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;
		}
	}
587

588
	return 0;
589 590
}

591
static int http_stream_write_chunked(
592 593 594
	git_smart_subtransport_stream *stream,
	const char *buffer,
	size_t len)
595
{
596
	http_stream *s = (http_stream *)stream;
597
	http_subtransport *t = OWNING_SUBTRANSPORT(s);
598

599
	assert(t->connected);
600

601
	/* Send the request, if necessary */
602
	if (!s->sent_request) {
603 604
		git_buf request = GIT_BUF_INIT;

605 606
		clear_parser_state(t);

607
		if (gen_request(&request, s, 0) < 0) {
608 609 610
			giterr_set(GITERR_NET, "Failed to generate request");
			return -1;
		}
611

612 613 614 615
		if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) {
			git_buf_free(&request);
			return -1;
		}
616

617
		git_buf_free(&request);
618

619 620
		s->sent_request = 1;
	}
621

622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
	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 */
637
		int count = min(CHUNK_SIZE - s->chunk_buffer_len, len);
638 639

		if (!s->chunk_buffer)
640
			s->chunk_buffer = git__malloc(CHUNK_SIZE);
641 642 643 644 645 646 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

		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;

696 697 698 699 700 701
	return 0;

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

703 704 705
static void http_stream_free(git_smart_subtransport_stream *stream)
{
	http_stream *s = (http_stream *)stream;
706

707 708 709
	if (s->chunk_buffer)
		git__free(s->chunk_buffer);

710 711 712
	if (s->redirect_url)
		git__free(s->redirect_url);

713 714
	git__free(s);
}
715

716 717
static int http_stream_alloc(http_subtransport *t,
	git_smart_subtransport_stream **stream)
718
{
719
	http_stream *s;
720

721 722
	if (!stream)
		return -1;
723

724
	s = git__calloc(sizeof(http_stream), 1);
725
	GITERR_CHECK_ALLOC(s);
726

727 728
	s->parent.subtransport = &t->parent;
	s->parent.read = http_stream_read;
729
	s->parent.write = http_stream_write_single;
730
	s->parent.free = http_stream_free;
731

732 733
	*stream = (git_smart_subtransport_stream *)s;
	return 0;
734 735
}

736 737 738
static int http_uploadpack_ls(
	http_subtransport *t,
	git_smart_subtransport_stream **stream)
739
{
740
	http_stream *s;
741

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

745
	s = (http_stream *)*stream;
746

747 748 749
	s->service = upload_pack_service;
	s->service_url = upload_pack_ls_service_url;
	s->verb = get_verb;
750

751
	return 0;
752 753
}

754 755 756
static int http_uploadpack(
	http_subtransport *t,
	git_smart_subtransport_stream **stream)
757
{
758
	http_stream *s;
759

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

763
	s = (http_stream *)*stream;
764

765 766 767
	s->service = upload_pack_service;
	s->service_url = upload_pack_service_url;
	s->verb = post_verb;
768

769
	return 0;
770 771
}

772 773 774 775 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
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;
}

812 813
static int http_action(
	git_smart_subtransport_stream **stream,
814
	git_smart_subtransport *subtransport,
815 816
	const char *url,
	git_smart_service_t action)
817
{
818
	http_subtransport *t = (http_subtransport *)subtransport;
819
	int ret;
820 821 822 823

	if (!stream)
		return -1;

824
	if ((!t->connection_data.host || !t->connection_data.port || !t->connection_data.path) &&
825
		 (ret = gitno_connection_data_from_url(&t->connection_data, url, NULL)) < 0)
826
		return ret;
827

828 829
	if (http_connect(t) < 0)
		return -1;
830

831 832 833
	switch (action) {
	case GIT_SERVICE_UPLOADPACK_LS:
		return http_uploadpack_ls(t, stream);
834

835 836
	case GIT_SERVICE_UPLOADPACK:
		return http_uploadpack(t, stream);
837

838 839
	case GIT_SERVICE_RECEIVEPACK_LS:
		return http_receivepack_ls(t, stream);
840

841 842
	case GIT_SERVICE_RECEIVEPACK:
		return http_receivepack(t, stream);
843
	}
844

845 846 847 848
	*stream = NULL;
	return -1;
}

849
static int http_close(git_smart_subtransport *subtransport)
850
{
851
	http_subtransport *t = (http_subtransport *) subtransport;
852

853 854
	clear_parser_state(t);

855
	if (t->socket.socket) {
856
		gitno_close(&t->socket);
857 858
		memset(&t->socket, 0x0, sizeof(gitno_socket));
	}
859

860 861 862 863 864
	if (t->cred) {
		t->cred->free(t->cred);
		t->cred = NULL;
	}

865 866 867 868 869
	if (t->url_cred) {
		t->url_cred->free(t->url_cred);
		t->url_cred = NULL;
	}

870
	gitno_connection_data_free_ptrs(&t->connection_data);
871

872 873 874 875 876 877 878 879 880
	return 0;
}

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

	http_close(subtransport);

881
	git__free(t);
882 883
}

Vicent Marti committed
884
int git_smart_subtransport_http(git_smart_subtransport **out, git_transport *owner)
885
{
886
	http_subtransport *t;
887

888 889
	if (!out)
		return -1;
890

891
	t = git__calloc(sizeof(http_subtransport), 1);
892
	GITERR_CHECK_ALLOC(t);
893

894
	t->owner = (transport_smart *)owner;
895
	t->parent.action = http_action;
896
	t->parent.close = http_close;
897
	t->parent.free = http_free;
898

899 900 901 902 903
	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;
904

905
	*out = (git_smart_subtransport *) t;
906 907
	return 0;
}
908 909

#endif /* !GIT_WINHTTP */