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

26 27
bool git_http__expect_continue = false;

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

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

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

typedef struct {
52
	git_net_url url;
53

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

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

63 64
	http_server server;
	http_server proxy;
65

66 67
	git_http_client *http_client;
} http_subtransport;
68

69 70 71 72
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",
73
	1,
74 75 76 77 78 79
	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",
80
	0,
81 82 83 84 85 86
	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",
87
	1,
88 89 90 91 92 93
	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",
94
	0,
95 96
	1
};
97

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

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

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

	if (!password)
		password = "";

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

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

	return GIT_PASSTHROUGH;
}

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

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

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

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

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

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

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

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

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

173
	return error;
174 175
}

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

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

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

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

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

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

222 223 224 225 226 227 228 229 230 231 232 233 234
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;
	}
}
235

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

245
	*complete = false;
246

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

330 331
		proxy = config;
		break;
332

333 334
	default:
		return 0;
335
	}
336

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

	if (!git_net_url_valid(&transport->proxy.url)) {
342 343
		git_error_set(GIT_ERROR_HTTP, "invalid URL: '%s'", proxy);
		error = -1;
344 345 346
		goto done;
	}

347
	*out_use = true;
348 349

done:
350
	git__free(config);
351 352 353
	return error;
}

354 355 356 357 358
static int generate_request(
	git_net_url *url,
	git_http_request *request,
	http_stream *stream,
	size_t len)
359
{
360 361
	http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
	bool use_proxy = false;
362
	int error;
363

364 365 366
	if ((error = git_net_url_joinpath(url,
		&transport->server.url, stream->service->url)) < 0 ||
	    (error = lookup_proxy(&use_proxy, transport)) < 0)
367 368
		return error;

369 370 371 372 373
	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;
374
	request->custom_headers = &transport->owner->connect_opts.custom_headers;
375

376 377 378 379 380 381
	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;
382
	}
383

384
	return 0;
385 386
}

387 388 389 390 391 392
/*
 * 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.
 */
393
static int http_stream_read(
394
	git_smart_subtransport_stream *s,
395
	char *buffer,
396 397 398 399 400 401 402 403 404 405 406
	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;
407

408
	*out_len = 0;
409

410 411 412
	if (stream->state == HTTP_STATE_NONE) {
		stream->state = HTTP_STATE_SENDING_REQUEST;
		stream->replay_count = 0;
413 414
	}

415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
	/*
	 * 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)
431
			goto done;
432

433 434
		if (complete)
			break;
435

436
		stream->replay_count++;
437
	}
438

439
	if (stream->state == HTTP_STATE_SENDING_REQUEST) {
440
		git_error_set(GIT_ERROR_HTTP, "too many redirects or authentication replays");
441
		error = GIT_ERROR; /* not GIT_EAUTH, because the exact cause is unclear */
442 443
		goto done;
	}
444

445
	GIT_ASSERT(stream->state == HTTP_STATE_RECEIVING_RESPONSE);
446

447
	error = git_http_client_read_body(transport->http_client, buffer, buffer_size);
448

449 450 451
	if (error > 0) {
		*out_len = error;
		error = 0;
452 453
	}

454
done:
455 456 457 458
	git_net_url_dispose(&url);
	git_net_url_dispose(&proxy_url);
	git_http_response_dispose(&response);

459
	return error;
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 507 508 509 510 511
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;
}

512 513 514 515 516 517 518 519 520 521
/*
* 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,
522 523
	const char *buffer,
	size_t len)
524
{
525 526 527 528 529 530
	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;
531

532 533
	while (stream->state == HTTP_STATE_NONE &&
	       stream->replay_count < GIT_HTTP_REPLAY_MAX) {
534

535 536
		git_net_url_dispose(&url);
		git_http_response_dispose(&response);
537

538 539 540 541 542 543 544 545 546 547 548 549 550 551
		/*
		 * 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. */
552 553 554 555
		if ((error = generate_request(&url, &request, stream, len)) < 0 ||
		    (error = git_http_client_send_request(
			transport->http_client, &request)) < 0)
			goto done;
556

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

573
		stream->replay_count++;
574
	}
575

576
	if (stream->state == HTTP_STATE_NONE) {
577
		git_error_set(GIT_ERROR_HTTP,
578
		              "too many redirects or authentication replays");
579
		error = GIT_ERROR; /* not GIT_EAUTH because the exact cause is unclear */
580
		goto done;
581 582
	}

583
	GIT_ASSERT(stream->state == HTTP_STATE_SENDING_REQUEST);
584

585
	error = git_http_client_send_body(transport->http_client, buffer, len);
586

587 588 589 590
done:
	git_http_response_dispose(&response);
	git_net_url_dispose(&url);
	return error;
591 592
}

593 594 595 596 597 598 599 600 601
/*
* 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)
602
{
603 604
	http_stream *stream = (http_stream *)s;
	http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
605
	git_http_client *client = transport->http_client;
606 607 608
	git_http_response response = {0};
	bool complete;
	int error;
609

610
	*out_len = 0;
611

612
	if (stream->state == HTTP_STATE_SENDING_REQUEST) {
613
		if ((error = git_http_client_read_response(&response, client)) < 0 ||
614 615
		    (error = handle_response(&complete, stream, &response, false)) < 0)
		    goto done;
616

617
		GIT_ASSERT(complete);
618 619
		stream->state = HTTP_STATE_RECEIVING_RESPONSE;
	}
620

621
	error = git_http_client_read_body(client, buffer, buffer_size);
622

623 624 625 626
	if (error > 0) {
		*out_len = error;
		error = 0;
	}
627

628 629 630
done:
	git_http_response_dispose(&response);
	return error;
631
}
632

633 634
static void http_stream_free(git_smart_subtransport_stream *stream)
{
635
	http_stream *s = GIT_CONTAINER_OF(stream, http_stream, parent);
636 637
	git__free(s);
}
638

639
static const http_service *select_service(git_smart_service_t action)
640
{
641 642 643 644 645 646 647 648 649 650
	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;
	}
651

652
	return NULL;
653 654
}

655
static int http_action(
656 657
	git_smart_subtransport_stream **out,
	git_smart_subtransport *t,
658 659
	const char *url,
	git_smart_service_t action)
660
{
661
	http_subtransport *transport = GIT_CONTAINER_OF(t, http_subtransport, parent);
662
	git_remote_connect_options *connect_opts = &transport->owner->connect_opts;
663
	git_http_client_options opts = {0};
664 665 666 667
	http_stream *stream;
	const http_service *service;
	int error;

668 669
	GIT_ASSERT_ARG(out);
	GIT_ASSERT_ARG(t);
670

671
	*out = NULL;
672

673 674 675 676 677 678 679
	/*
	 * 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).
	 */
680 681 682
	if (!git_net_url_valid(&transport->server.url) &&
	    (error = git_net_url_parse(&transport->server.url, url)) < 0)
		return error;
683

684
	if ((service = select_service(action)) == NULL) {
685
		git_error_set(GIT_ERROR_HTTP, "invalid action");
686 687
		return -1;
	}
688

689 690
	stream = git__calloc(sizeof(http_stream), 1);
	GIT_ERROR_CHECK_ALLOC(stream);
691

692 693 694 695
	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;
696

697 698 699
	if (transport->http_client) {
		git_http_client_set_options(transport->http_client, &opts);
	} else {
700 701
		if (git_http_client_new(&transport->http_client, &opts) < 0)
			return -1;
702
	}
703

704 705
	stream->service = service;
	stream->parent.subtransport = &transport->parent;
706

707 708 709 710 711
	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;
712
	}
713

714
	stream->parent.free = http_stream_free;
715

716 717 718
	*out = (git_smart_subtransport_stream *)stream;
	return 0;
}
719

720 721 722
static int http_close(git_smart_subtransport *t)
{
	http_subtransport *transport = GIT_CONTAINER_OF(t, http_subtransport, parent);
723

724 725
	free_cred(&transport->server.cred);
	free_cred(&transport->proxy.cred);
726

727 728
	transport->server.url_cred_presented = false;
	transport->proxy.url_cred_presented = false;
729

730 731
	git_net_url_dispose(&transport->server.url);
	git_net_url_dispose(&transport->proxy.url);
732

733 734 735
	return 0;
}

736
static void http_free(git_smart_subtransport *t)
737
{
738
	http_subtransport *transport = GIT_CONTAINER_OF(t, http_subtransport, parent);
739

740 741 742 743
	git_http_client_free(transport->http_client);

	http_close(t);
	git__free(transport);
744 745
}

746
int git_smart_subtransport_http(git_smart_subtransport **out, git_transport *owner, void *param)
747
{
748
	http_subtransport *transport;
749

750 751
	GIT_UNUSED(param);

752
	GIT_ASSERT_ARG(out);
753

754 755
	transport = git__calloc(sizeof(http_subtransport), 1);
	GIT_ERROR_CHECK_ALLOC(transport);
756

757 758 759 760
	transport->owner = (transport_smart *)owner;
	transport->parent.action = http_action;
	transport->parent.close = http_close;
	transport->parent.free = http_free;
761

762
	*out = (git_smart_subtransport *) transport;
763 764
	return 0;
}
765 766

#endif /* !GIT_WINHTTP */