http.c 20.3 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
#include "http_parser.h"
13
#include "net.h"
14
#include "netops.h"
15
#include "remote.h"
16
#include "smart.h"
17
#include "auth.h"
18
#include "http.h"
19
#include "auth_negotiate.h"
20
#include "auth_ntlm.h"
21
#include "trace.h"
22 23
#include "streams/tls.h"
#include "streams/socket.h"
24
#include "httpclient.h"
25
#include "git2/sys/credential.h"
26

27 28
bool git_http__expect_continue = false;

29 30 31 32 33 34
typedef enum {
	HTTP_STATE_NONE = 0,
	HTTP_STATE_SENDING_REQUEST,
	HTTP_STATE_RECEIVING_RESPONSE,
	HTTP_STATE_DONE
} http_state;
35

36 37 38 39 40
typedef struct {
	git_http_method method;
	const char *url;
	const char *request_type;
	const char *response_type;
41 42
	unsigned int initial : 1,
	             chunked : 1;
43
} http_service;
44

45
typedef struct {
46
	git_smart_subtransport_stream parent;
47 48 49
	const http_service *service;
	http_state state;
	unsigned replay_count;
50 51 52
} http_stream;

typedef struct {
53
	git_net_url url;
54

55
	git_credential *cred;
56 57
	unsigned auth_schemetypes;
	unsigned url_cred_presented : 1;
58 59 60
} http_server;

typedef struct {
61
	git_smart_subtransport parent;
62
	transport_smart *owner;
63

64 65
	http_server server;
	http_server proxy;
66

67 68
	git_http_client *http_client;
} http_subtransport;
69

70 71 72 73
static const http_service upload_pack_ls_service = {
	GIT_HTTP_METHOD_GET, "/info/refs?service=git-upload-pack",
	NULL,
	"application/x-git-upload-pack-advertisement",
74
	1,
75 76 77 78 79 80
	0
};
static const http_service upload_pack_service = {
	GIT_HTTP_METHOD_POST, "/git-upload-pack",
	"application/x-git-upload-pack-request",
	"application/x-git-upload-pack-result",
81
	0,
82 83 84 85 86 87
	0
};
static const http_service receive_pack_ls_service = {
	GIT_HTTP_METHOD_GET, "/info/refs?service=git-receive-pack",
	NULL,
	"application/x-git-receive-pack-advertisement",
88
	1,
89 90 91 92 93 94
	0
};
static const http_service receive_pack_service = {
	GIT_HTTP_METHOD_POST, "/git-receive-pack",
	"application/x-git-receive-pack-request",
	"application/x-git-receive-pack-result",
95
	0,
96 97
	1
};
98

99 100
#define SERVER_TYPE_REMOTE "remote"
#define SERVER_TYPE_PROXY  "proxy"
101

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

104
static int apply_url_credentials(
105
	git_credential **cred,
106 107 108 109
	unsigned int allowed_types,
	const char *username,
	const char *password)
{
110 111 112 113 114
	GIT_ASSERT_ARG(username);

	if (!password)
		password = "";

115 116
	if (allowed_types & GIT_CREDENTIAL_USERPASS_PLAINTEXT)
		return git_credential_userpass_plaintext_new(cred, username, password);
117

118 119
	if ((allowed_types & GIT_CREDENTIAL_DEFAULT) && *username == '\0' && *password == '\0')
		return git_credential_default_new(cred);
120 121 122 123

	return GIT_PASSTHROUGH;
}

124
GIT_INLINE(void) free_cred(git_credential **cred)
125
{
126
	if (*cred) {
127
		git_credential_free(*cred);
128
		(*cred) = NULL;
129 130 131
	}
}

132
static int handle_auth(
133
	http_server *server,
134
	const char *server_type,
135
	const char *url,
136 137
	unsigned int allowed_schemetypes,
	unsigned int allowed_credtypes,
138
	git_credential_acquire_cb callback,
139
	void *callback_payload)
140
{
141
	int error = 1;
142

143 144
	if (server->cred)
		free_cred(&server->cred);
145 146

	/* Start with URL-specified credentials, if there were any. */
147
	if ((allowed_credtypes & GIT_CREDENTIAL_USERPASS_PLAINTEXT) &&
148
	    !server->url_cred_presented &&
149
	    server->url.username) {
150
		error = apply_url_credentials(&server->cred, allowed_credtypes, server->url.username, server->url.password);
151
		server->url_cred_presented = 1;
152

153 154
		/* treat GIT_PASSTHROUGH as if callback isn't set */
		if (error == GIT_PASSTHROUGH)
155 156
			error = 1;
	}
157

158
	if (error > 0 && callback) {
159
		error = callback(&server->cred, url, server->url.username, allowed_credtypes, callback_payload);
160

161 162
		/* treat GIT_PASSTHROUGH as if callback isn't set */
		if (error == GIT_PASSTHROUGH)
163
			error = 1;
164 165
	}

166
	if (error > 0) {
167
		git_error_set(GIT_ERROR_HTTP, "%s authentication required but no callback set", server_type);
168
		error = GIT_EAUTH;
169 170
	}

171 172
	if (!error)
		server->auth_schemetypes = allowed_schemetypes;
173

174
	return error;
175 176
}

177 178 179
GIT_INLINE(int) handle_remote_auth(
	http_stream *stream,
	git_http_response *response)
180
{
181
	http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
182
	git_remote_connect_options *connect_opts = &transport->owner->connect_opts;
183

184
	if (response->server_auth_credtypes == 0) {
185
		git_error_set(GIT_ERROR_HTTP, "server requires authentication that we do not support");
186
		return GIT_EAUTH;
187
	}
188

189 190 191 192 193 194 195
	/* Otherwise, prompt for credentials. */
	return handle_auth(
		&transport->server,
		SERVER_TYPE_REMOTE,
		transport->owner->url,
		response->server_auth_schemetypes,
		response->server_auth_credtypes,
196 197
		connect_opts->callbacks.credentials,
		connect_opts->callbacks.payload);
198 199
}

200 201 202
GIT_INLINE(int) handle_proxy_auth(
	http_stream *stream,
	git_http_response *response)
203
{
204
	http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
205
	git_remote_connect_options *connect_opts = &transport->owner->connect_opts;
206

207
	if (response->proxy_auth_credtypes == 0) {
208
		git_error_set(GIT_ERROR_HTTP, "proxy requires authentication that we do not support");
209
		return GIT_EAUTH;
210
	}
211

212 213 214 215
	/* Otherwise, prompt for credentials. */
	return handle_auth(
		&transport->proxy,
		SERVER_TYPE_PROXY,
216
		connect_opts->proxy_opts.url,
217 218
		response->server_auth_schemetypes,
		response->proxy_auth_credtypes,
219 220
		connect_opts->proxy_opts.credentials,
		connect_opts->proxy_opts.payload);
221
}
222

223 224 225 226 227 228 229 230 231 232 233 234 235
static bool allow_redirect(http_stream *stream)
{
	http_subtransport *transport = OWNING_SUBTRANSPORT(stream);

	switch (transport->owner->connect_opts.follow_redirects) {
	case GIT_REMOTE_REDIRECT_INITIAL:
		return (stream->service->initial == 1);
	case GIT_REMOTE_REDIRECT_ALL:
		return true;
	default:
		return false;
	}
}
236

237 238 239 240 241
static int handle_response(
	bool *complete,
	http_stream *stream,
	git_http_response *response,
	bool allow_replay)
242
{
243 244
	http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
	int error;
245

246
	*complete = false;
247

248 249
	if (allow_replay && git_http_response_is_redirect(response)) {
		if (!response->location) {
250
			git_error_set(GIT_ERROR_HTTP, "redirect without location");
251 252
			return -1;
		}
253

254
		if (git_net_url_apply_redirect(&transport->server.url, response->location, allow_redirect(stream), stream->service->url) < 0) {
255 256
			return -1;
		}
257

258 259
		return 0;
	} else if (git_http_response_is_redirect(response)) {
260
		git_error_set(GIT_ERROR_HTTP, "unexpected redirect");
261
		return -1;
262
	}
263

264 265
	/* If we're in the middle of challenge/response auth, continue. */
	if (allow_replay && response->resend_credentials) {
266
		return 0;
267
	} else if (allow_replay && response->status == GIT_HTTP_STATUS_UNAUTHORIZED) {
268
		if ((error = handle_remote_auth(stream, response)) < 0)
269 270
			return error;

271
		return git_http_client_skip_body(transport->http_client);
272
	} else if (allow_replay && response->status == GIT_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED) {
273 274
		if ((error = handle_proxy_auth(stream, response)) < 0)
			return error;
275

276
		return git_http_client_skip_body(transport->http_client);
277 278
	} else if (response->status == GIT_HTTP_STATUS_UNAUTHORIZED ||
	           response->status == GIT_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED) {
279
		git_error_set(GIT_ERROR_HTTP, "unexpected authentication failure");
280
		return GIT_EAUTH;
281 282
	}

283
	if (response->status != GIT_HTTP_STATUS_OK) {
284
		git_error_set(GIT_ERROR_HTTP, "unexpected http status code: %d", response->status);
285
		return -1;
286 287
	}

288 289
	/* The response must contain a Content-Type header. */
	if (!response->content_type) {
290
		git_error_set(GIT_ERROR_HTTP, "no content-type header in response");
291
		return -1;
292
	}
293

294 295
	/* The Content-Type header must match our expectation. */
	if (strcmp(response->content_type, stream->service->response_type) != 0) {
296
		git_error_set(GIT_ERROR_HTTP, "invalid content-type: '%s'", response->content_type);
297
		return -1;
298 299
	}

300 301
	*complete = true;
	stream->state = HTTP_STATE_RECEIVING_RESPONSE;
302 303 304
	return 0;
}

305 306 307
static int lookup_proxy(
	bool *out_use,
	http_subtransport *transport)
308
{
309
	git_remote_connect_options *connect_opts = &transport->owner->connect_opts;
310 311 312 313
	const char *proxy;
	git_remote *remote;
	char *config = NULL;
	int error = 0;
314

315 316
	*out_use = false;
	git_net_url_dispose(&transport->proxy.url);
317

318
	switch (connect_opts->proxy_opts.type) {
319
	case GIT_PROXY_SPECIFIED:
320
		proxy = connect_opts->proxy_opts.url;
321
		break;
322

323 324
	case GIT_PROXY_AUTO:
		remote = transport->owner->owner;
325

326
		error = git_remote__http_proxy(&config, remote, &transport->server.url);
327

328
		if (error || !config)
329
			goto done;
330

331 332
		proxy = config;
		break;
333

334 335
	default:
		return 0;
336
	}
337

338 339 340
	if (!proxy ||
	    (error = git_net_url_parse(&transport->proxy.url, proxy)) < 0)
		goto done;
341

342
	*out_use = true;
343 344

done:
345
	git__free(config);
346 347 348
	return error;
}

349 350 351 352 353
static int generate_request(
	git_net_url *url,
	git_http_request *request,
	http_stream *stream,
	size_t len)
354
{
355 356
	http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
	bool use_proxy = false;
357
	int error;
358

359 360 361
	if ((error = git_net_url_joinpath(url,
		&transport->server.url, stream->service->url)) < 0 ||
	    (error = lookup_proxy(&use_proxy, transport)) < 0)
362 363
		return error;

364 365 366 367 368
	request->method = stream->service->method;
	request->url = url;
	request->credentials = transport->server.cred;
	request->proxy = use_proxy ? &transport->proxy.url : NULL;
	request->proxy_credentials = transport->proxy.cred;
369
	request->custom_headers = &transport->owner->connect_opts.custom_headers;
370

371 372 373 374 375 376
	if (stream->service->method == GIT_HTTP_METHOD_POST) {
		request->chunked = stream->service->chunked;
		request->content_length = stream->service->chunked ? 0 : len;
		request->content_type = stream->service->request_type;
		request->accept = stream->service->response_type;
		request->expect_continue = git_http__expect_continue;
377
	}
378

379
	return 0;
380 381
}

382 383 384 385 386 387
/*
 * Read from an HTTP transport - for the first invocation of this function
 * (ie, when stream->state == HTTP_STATE_NONE), we'll send a GET request
 * to the remote host.  We will stream that data back on all subsequent
 * calls.
 */
388
static int http_stream_read(
389
	git_smart_subtransport_stream *s,
390
	char *buffer,
391 392 393 394 395 396 397 398 399 400 401
	size_t buffer_size,
	size_t *out_len)
{
	http_stream *stream = (http_stream *)s;
	http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
	git_net_url url = GIT_NET_URL_INIT;
	git_net_url proxy_url = GIT_NET_URL_INIT;
	git_http_request request = {0};
	git_http_response response = {0};
	bool complete;
	int error;
402

403
	*out_len = 0;
404

405 406 407
	if (stream->state == HTTP_STATE_NONE) {
		stream->state = HTTP_STATE_SENDING_REQUEST;
		stream->replay_count = 0;
408 409
	}

410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
	/*
	 * Formulate the URL, send the request and read the response
	 * headers.  Some of the request body may also be read.
	 */
	while (stream->state == HTTP_STATE_SENDING_REQUEST &&
	       stream->replay_count < GIT_HTTP_REPLAY_MAX) {
		git_net_url_dispose(&url);
		git_net_url_dispose(&proxy_url);
		git_http_response_dispose(&response);

		if ((error = generate_request(&url, &request, stream, 0)) < 0 ||
		    (error = git_http_client_send_request(
			transport->http_client, &request)) < 0 ||
		    (error = git_http_client_read_response(
			    &response, transport->http_client)) < 0 ||
		    (error = handle_response(&complete, stream, &response, true)) < 0)
426
			goto done;
427

428 429
		if (complete)
			break;
430

431
		stream->replay_count++;
432
	}
433

434
	if (stream->state == HTTP_STATE_SENDING_REQUEST) {
435
		git_error_set(GIT_ERROR_HTTP, "too many redirects or authentication replays");
436
		error = GIT_ERROR; /* not GIT_EAUTH, because the exact cause is unclear */
437 438
		goto done;
	}
439

440
	GIT_ASSERT(stream->state == HTTP_STATE_RECEIVING_RESPONSE);
441

442
	error = git_http_client_read_body(transport->http_client, buffer, buffer_size);
443

444 445 446
	if (error > 0) {
		*out_len = error;
		error = 0;
447 448
	}

449
done:
450 451 452 453
	git_net_url_dispose(&url);
	git_net_url_dispose(&proxy_url);
	git_http_response_dispose(&response);

454
	return error;
455 456
}

457 458 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 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
static bool needs_probe(http_stream *stream)
{
	http_subtransport *transport = OWNING_SUBTRANSPORT(stream);

	return (transport->server.auth_schemetypes == GIT_HTTP_AUTH_NTLM ||
	        transport->server.auth_schemetypes == GIT_HTTP_AUTH_NEGOTIATE);
}

static int send_probe(http_stream *stream)
{
	http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
	git_http_client *client = transport->http_client;
	const char *probe = "0000";
	size_t len = 4;
	git_net_url url = GIT_NET_URL_INIT;
	git_http_request request = {0};
	git_http_response response = {0};
	bool complete = false;
	size_t step, steps = 1;
	int error;

	/* NTLM requires a full challenge/response */
	if (transport->server.auth_schemetypes == GIT_HTTP_AUTH_NTLM)
		steps = GIT_AUTH_STEPS_NTLM;

	/*
	 * Send at most two requests: one without any authentication to see
	 * if we get prompted to authenticate.  If we do, send a second one
	 * with the first authentication message.  The final authentication
	 * message with the response will occur with the *actual* POST data.
	 */
	for (step = 0; step < steps && !complete; step++) {
		git_net_url_dispose(&url);
		git_http_response_dispose(&response);

		if ((error = generate_request(&url, &request, stream, len)) < 0 ||
		    (error = git_http_client_send_request(client, &request)) < 0 ||
		    (error = git_http_client_send_body(client, probe, len)) < 0 ||
		    (error = git_http_client_read_response(&response, client)) < 0 ||
		    (error = git_http_client_skip_body(client)) < 0 ||
		    (error = handle_response(&complete, stream, &response, true)) < 0)
			goto done;
	}

done:
	git_http_response_dispose(&response);
	git_net_url_dispose(&url);
	return error;
}

507 508 509 510 511 512 513 514 515 516
/*
* Write to an HTTP transport - for the first invocation of this function
* (ie, when stream->state == HTTP_STATE_NONE), we'll send a POST request
* to the remote host.  If we're sending chunked data, then subsequent calls
* will write the additional data given in the buffer.  If we're not chunking,
* then the caller should have given us all the data in the original call.
* The caller should call http_stream_read_response to get the result.
*/
static int http_stream_write(
	git_smart_subtransport_stream *s,
517 518
	const char *buffer,
	size_t len)
519
{
520 521 522 523 524 525
	http_stream *stream = GIT_CONTAINER_OF(s, http_stream, parent);
	http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
	git_net_url url = GIT_NET_URL_INIT;
	git_http_request request = {0};
	git_http_response response = {0};
	int error;
526

527 528
	while (stream->state == HTTP_STATE_NONE &&
	       stream->replay_count < GIT_HTTP_REPLAY_MAX) {
529

530 531
		git_net_url_dispose(&url);
		git_http_response_dispose(&response);
532

533 534 535 536 537 538 539 540 541 542 543 544 545 546
		/*
		 * If we're authenticating with a connection-based mechanism
		 * (NTLM, Kerberos), send a "probe" packet.  Servers SHOULD
		 * authenticate an entire keep-alive connection, so ideally
		 * we should not need to authenticate but some servers do
		 * not support this.  By sending a probe packet, we'll be
		 * able to follow up with a second POST using the actual
		 * data (and, in the degenerate case, the authentication
		 * header as well).
		 */
		if (needs_probe(stream) && (error = send_probe(stream)) < 0)
			goto done;

		/* Send the regular POST request. */
547 548 549 550
		if ((error = generate_request(&url, &request, stream, len)) < 0 ||
		    (error = git_http_client_send_request(
			transport->http_client, &request)) < 0)
			goto done;
551

552 553 554 555 556 557 558 559 560 561 562 563 564 565
		if (request.expect_continue &&
		    git_http_client_has_response(transport->http_client)) {
			bool complete;

			/*
			 * If we got a response to an expect/continue, then
			 * it's something other than a 100 and we should
			 * deal with the response somehow.
			 */
			if ((error = git_http_client_read_response(&response, transport->http_client)) < 0 ||
			    (error = handle_response(&complete, stream, &response, true)) < 0)
			    goto done;
		} else {
			stream->state = HTTP_STATE_SENDING_REQUEST;
566
		}
567

568
		stream->replay_count++;
569
	}
570

571
	if (stream->state == HTTP_STATE_NONE) {
572
		git_error_set(GIT_ERROR_HTTP,
573
		              "too many redirects or authentication replays");
574
		error = GIT_ERROR; /* not GIT_EAUTH because the exact cause is unclear */
575
		goto done;
576 577
	}

578
	GIT_ASSERT(stream->state == HTTP_STATE_SENDING_REQUEST);
579

580
	error = git_http_client_send_body(transport->http_client, buffer, len);
581

582 583 584 585
done:
	git_http_response_dispose(&response);
	git_net_url_dispose(&url);
	return error;
586 587
}

588 589 590 591 592 593 594 595 596
/*
* Read from an HTTP transport after it has been written to.  This is the
* response from a POST request made by http_stream_write.
*/
static int http_stream_read_response(
	git_smart_subtransport_stream *s,
	char *buffer,
	size_t buffer_size,
	size_t *out_len)
597
{
598 599
	http_stream *stream = (http_stream *)s;
	http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
600
	git_http_client *client = transport->http_client;
601 602 603
	git_http_response response = {0};
	bool complete;
	int error;
604

605
	*out_len = 0;
606

607
	if (stream->state == HTTP_STATE_SENDING_REQUEST) {
608
		if ((error = git_http_client_read_response(&response, client)) < 0 ||
609 610
		    (error = handle_response(&complete, stream, &response, false)) < 0)
		    goto done;
611

612
		GIT_ASSERT(complete);
613 614
		stream->state = HTTP_STATE_RECEIVING_RESPONSE;
	}
615

616
	error = git_http_client_read_body(client, buffer, buffer_size);
617

618 619 620 621
	if (error > 0) {
		*out_len = error;
		error = 0;
	}
622

623 624 625
done:
	git_http_response_dispose(&response);
	return error;
626
}
627

628 629
static void http_stream_free(git_smart_subtransport_stream *stream)
{
630
	http_stream *s = GIT_CONTAINER_OF(stream, http_stream, parent);
631 632
	git__free(s);
}
633

634
static const http_service *select_service(git_smart_service_t action)
635
{
636 637 638 639 640 641 642 643 644 645
	switch (action) {
	case GIT_SERVICE_UPLOADPACK_LS:
		return &upload_pack_ls_service;
	case GIT_SERVICE_UPLOADPACK:
		return &upload_pack_service;
	case GIT_SERVICE_RECEIVEPACK_LS:
		return &receive_pack_ls_service;
	case GIT_SERVICE_RECEIVEPACK:
		return &receive_pack_service;
	}
646

647
	return NULL;
648 649
}

650
static int http_action(
651 652
	git_smart_subtransport_stream **out,
	git_smart_subtransport *t,
653 654
	const char *url,
	git_smart_service_t action)
655
{
656
	http_subtransport *transport = GIT_CONTAINER_OF(t, http_subtransport, parent);
657
	git_remote_connect_options *connect_opts = &transport->owner->connect_opts;
658 659 660 661
	http_stream *stream;
	const http_service *service;
	int error;

662 663
	GIT_ASSERT_ARG(out);
	GIT_ASSERT_ARG(t);
664

665
	*out = NULL;
666

667 668 669 670 671 672 673
	/*
	 * If we've seen a redirect then preserve the location that we've
	 * been given.  This is important to continue authorization against
	 * the redirect target, not the user-given source; the endpoint may
	 * have redirected us from HTTP->HTTPS and is using an auth mechanism
	 * that would be insecure in plaintext (eg, HTTP Basic).
	 */
674 675 676
	if (!git_net_url_valid(&transport->server.url) &&
	    (error = git_net_url_parse(&transport->server.url, url)) < 0)
		return error;
677

678
	if ((service = select_service(action)) == NULL) {
679
		git_error_set(GIT_ERROR_HTTP, "invalid action");
680 681
		return -1;
	}
682

683 684
	stream = git__calloc(sizeof(http_stream), 1);
	GIT_ERROR_CHECK_ALLOC(stream);
685

686 687
	if (!transport->http_client) {
		git_http_client_options opts = {0};
688

689 690 691 692
		opts.server_certificate_check_cb = connect_opts->callbacks.certificate_check;
		opts.server_certificate_check_payload = connect_opts->callbacks.payload;
		opts.proxy_certificate_check_cb = connect_opts->proxy_opts.certificate_check;
		opts.proxy_certificate_check_payload = connect_opts->proxy_opts.payload;
693

694 695
		if (git_http_client_new(&transport->http_client, &opts) < 0)
			return -1;
696
	}
697

698 699
	stream->service = service;
	stream->parent.subtransport = &transport->parent;
700

701 702 703 704 705
	if (service->method == GIT_HTTP_METHOD_GET) {
		stream->parent.read = http_stream_read;
	} else {
		stream->parent.write = http_stream_write;
		stream->parent.read = http_stream_read_response;
706
	}
707

708
	stream->parent.free = http_stream_free;
709

710 711 712
	*out = (git_smart_subtransport_stream *)stream;
	return 0;
}
713

714 715 716
static int http_close(git_smart_subtransport *t)
{
	http_subtransport *transport = GIT_CONTAINER_OF(t, http_subtransport, parent);
717

718 719
	free_cred(&transport->server.cred);
	free_cred(&transport->proxy.cred);
720

721 722
	transport->server.url_cred_presented = false;
	transport->proxy.url_cred_presented = false;
723

724 725
	git_net_url_dispose(&transport->server.url);
	git_net_url_dispose(&transport->proxy.url);
726

727 728 729
	return 0;
}

730
static void http_free(git_smart_subtransport *t)
731
{
732
	http_subtransport *transport = GIT_CONTAINER_OF(t, http_subtransport, parent);
733

734 735 736 737
	git_http_client_free(transport->http_client);

	http_close(t);
	git__free(transport);
738 739
}

740
int git_smart_subtransport_http(git_smart_subtransport **out, git_transport *owner, void *param)
741
{
742
	http_subtransport *transport;
743

744 745
	GIT_UNUSED(param);

746
	GIT_ASSERT_ARG(out);
747

748 749
	transport = git__calloc(sizeof(http_subtransport), 1);
	GIT_ERROR_CHECK_ALLOC(transport);
750

751 752 753 754
	transport->owner = (transport_smart *)owner;
	transport->parent.action = http_action;
	transport->parent.close = http_close;
	transport->parent.free = http_free;
755

756
	*out = (git_smart_subtransport *) transport;
757 758
	return 0;
}
759 760

#endif /* !GIT_WINHTTP */