http.c 19.6 KB
Newer Older
1
/*
Edward Thomson committed
2
 * Copyright (C) the libgit2 contributors. All rights reserved.
3
 *
4 5
 * This file is part of libgit2, distributed under the GNU GPL v2 with
 * a Linking Exception. For full terms see the included COPYING file.
6
 */
7 8 9

#include "common.h"

10 11
#ifndef GIT_WINHTTP

12 13 14
#include "git2.h"
#include "http_parser.h"
#include "buffer.h"
15
#include "net.h"
16
#include "netops.h"
17
#include "remote.h"
18
#include "git2/sys/credential.h"
19
#include "smart.h"
20
#include "auth.h"
21
#include "http.h"
22
#include "auth_negotiate.h"
23
#include "auth_ntlm.h"
24
#include "trace.h"
25 26
#include "streams/tls.h"
#include "streams/socket.h"
27
#include "httpclient.h"
28

29 30
bool git_http__expect_continue = false;

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

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

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

typedef struct {
54
	git_net_url url;
55

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

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

65 66
	http_server server;
	http_server proxy;
67

68 69
	git_http_client *http_client;
} http_subtransport;
70

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

96 97
#define SERVER_TYPE_REMOTE "remote"
#define SERVER_TYPE_PROXY  "proxy"
98

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

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

	if (!password)
		password = "";

112 113
	if (allowed_types & GIT_CREDENTIAL_USERPASS_PLAINTEXT)
		return git_credential_userpass_plaintext_new(cred, username, password);
114

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

	return GIT_PASSTHROUGH;
}

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

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

140 141
	if (server->cred)
		free_cred(&server->cred);
142 143

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

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

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

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

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

168 169
	if (!error)
		server->auth_schemetypes = allowed_schemetypes;
170

171
	return error;
172 173
}

174 175 176
GIT_INLINE(int) handle_remote_auth(
	http_stream *stream,
	git_http_response *response)
177
{
178
	http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
179

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

185 186 187 188 189 190 191 192 193
	/* Otherwise, prompt for credentials. */
	return handle_auth(
		&transport->server,
		SERVER_TYPE_REMOTE,
		transport->owner->url,
		response->server_auth_schemetypes,
		response->server_auth_credtypes,
		transport->owner->cred_acquire_cb,
		transport->owner->cred_acquire_payload);
194 195
}

196 197 198
GIT_INLINE(int) handle_proxy_auth(
	http_stream *stream,
	git_http_response *response)
199
{
200
	http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
201

202
	if (response->proxy_auth_credtypes == 0) {
203
		git_error_set(GIT_ERROR_HTTP, "proxy requires authentication that we do not support");
204
		return GIT_EAUTH;
205
	}
206

207 208 209 210 211 212 213 214 215
	/* Otherwise, prompt for credentials. */
	return handle_auth(
		&transport->proxy,
		SERVER_TYPE_PROXY,
		transport->owner->proxy.url,
		response->server_auth_schemetypes,
		response->proxy_auth_credtypes,
		transport->owner->proxy.credentials,
		transport->owner->proxy.payload);
216
}
217

218

219 220 221 222 223
static int handle_response(
	bool *complete,
	http_stream *stream,
	git_http_response *response,
	bool allow_replay)
224
{
225 226
	http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
	int error;
227

228
	*complete = false;
229

230 231
	if (allow_replay && git_http_response_is_redirect(response)) {
		if (!response->location) {
232
			git_error_set(GIT_ERROR_HTTP, "redirect without location");
233 234
			return -1;
		}
235

236 237 238
		if (git_net_url_apply_redirect(&transport->server.url, response->location, stream->service->url) < 0) {
			return -1;
		}
239

240 241
		return 0;
	} else if (git_http_response_is_redirect(response)) {
242
		git_error_set(GIT_ERROR_HTTP, "unexpected redirect");
243
		return -1;
244
	}
245

246 247
	/* If we're in the middle of challenge/response auth, continue. */
	if (allow_replay && response->resend_credentials) {
248
		return 0;
249
	} else if (allow_replay && response->status == GIT_HTTP_STATUS_UNAUTHORIZED) {
250
		if ((error = handle_remote_auth(stream, response)) < 0)
251 252
			return error;

253
		return git_http_client_skip_body(transport->http_client);
254
	} else if (allow_replay && response->status == GIT_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED) {
255 256
		if ((error = handle_proxy_auth(stream, response)) < 0)
			return error;
257

258
		return git_http_client_skip_body(transport->http_client);
259 260
	} else if (response->status == GIT_HTTP_STATUS_UNAUTHORIZED ||
	           response->status == GIT_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED) {
261
		git_error_set(GIT_ERROR_HTTP, "unexpected authentication failure");
262
		return GIT_EAUTH;
263 264
	}

265
	if (response->status != GIT_HTTP_STATUS_OK) {
266
		git_error_set(GIT_ERROR_HTTP, "unexpected http status code: %d", response->status);
267
		return -1;
268 269
	}

270 271
	/* The response must contain a Content-Type header. */
	if (!response->content_type) {
272
		git_error_set(GIT_ERROR_HTTP, "no content-type header in response");
273
		return -1;
274
	}
275

276 277
	/* The Content-Type header must match our expectation. */
	if (strcmp(response->content_type, stream->service->response_type) != 0) {
278
		git_error_set(GIT_ERROR_HTTP, "invalid content-type: '%s'", response->content_type);
279
		return -1;
280 281
	}

282 283
	*complete = true;
	stream->state = HTTP_STATE_RECEIVING_RESPONSE;
284 285 286
	return 0;
}

287 288 289
static int lookup_proxy(
	bool *out_use,
	http_subtransport *transport)
290
{
291 292 293 294
	const char *proxy;
	git_remote *remote;
	char *config = NULL;
	int error = 0;
295

296 297
	*out_use = false;
	git_net_url_dispose(&transport->proxy.url);
298

299 300 301 302
	switch (transport->owner->proxy.type) {
	case GIT_PROXY_SPECIFIED:
		proxy = transport->owner->proxy.url;
		break;
303

304 305
	case GIT_PROXY_AUTO:
		remote = transport->owner->owner;
306

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

309
		if (error || !config)
310
			goto done;
311

312 313
		proxy = config;
		break;
314

315 316
	default:
		return 0;
317
	}
318

319 320 321
	if (!proxy ||
	    (error = git_net_url_parse(&transport->proxy.url, proxy)) < 0)
		goto done;
322

323
	*out_use = true;
324 325

done:
326
	git__free(config);
327 328 329
	return error;
}

330 331 332 333 334
static int generate_request(
	git_net_url *url,
	git_http_request *request,
	http_stream *stream,
	size_t len)
335
{
336 337
	http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
	bool use_proxy = false;
338
	int error;
339

340 341 342
	if ((error = git_net_url_joinpath(url,
		&transport->server.url, stream->service->url)) < 0 ||
	    (error = lookup_proxy(&use_proxy, transport)) < 0)
343 344
		return error;

345 346 347 348 349
	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;
350
	request->custom_headers = &transport->owner->custom_headers;
351

352 353 354 355 356 357
	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;
358
	}
359

360
	return 0;
361 362
}

363 364 365 366 367 368
/*
 * 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.
 */
369
static int http_stream_read(
370
	git_smart_subtransport_stream *s,
371
	char *buffer,
372 373 374 375 376 377 378 379 380 381 382
	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;
383

384
	*out_len = 0;
385

386 387 388
	if (stream->state == HTTP_STATE_NONE) {
		stream->state = HTTP_STATE_SENDING_REQUEST;
		stream->replay_count = 0;
389 390
	}

391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406
	/*
	 * 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)
407
			goto done;
408

409 410
		if (complete)
			break;
411

412
		stream->replay_count++;
413
	}
414

415
	if (stream->state == HTTP_STATE_SENDING_REQUEST) {
416
		git_error_set(GIT_ERROR_HTTP, "too many redirects or authentication replays");
417
		error = GIT_ERROR; /* not GIT_EAUTH, because the exact cause is unclear */
418 419
		goto done;
	}
420

421
	GIT_ASSERT(stream->state == HTTP_STATE_RECEIVING_RESPONSE);
422

423
	error = git_http_client_read_body(transport->http_client, buffer, buffer_size);
424

425 426 427
	if (error > 0) {
		*out_len = error;
		error = 0;
428 429
	}

430
done:
431 432 433 434
	git_net_url_dispose(&url);
	git_net_url_dispose(&proxy_url);
	git_http_response_dispose(&response);

435
	return error;
436 437
}

438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 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
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;
}

488 489 490 491 492 493 494 495 496 497
/*
* 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,
498 499
	const char *buffer,
	size_t len)
500
{
501 502 503 504 505 506
	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;
507

508 509
	while (stream->state == HTTP_STATE_NONE &&
	       stream->replay_count < GIT_HTTP_REPLAY_MAX) {
510

511 512
		git_net_url_dispose(&url);
		git_http_response_dispose(&response);
513

514 515 516 517 518 519 520 521 522 523 524 525 526 527
		/*
		 * 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. */
528 529 530 531
		if ((error = generate_request(&url, &request, stream, len)) < 0 ||
		    (error = git_http_client_send_request(
			transport->http_client, &request)) < 0)
			goto done;
532

533 534 535 536 537 538 539 540 541 542 543 544 545 546
		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;
547
		}
548

549
		stream->replay_count++;
550
	}
551

552
	if (stream->state == HTTP_STATE_NONE) {
553
		git_error_set(GIT_ERROR_HTTP,
554
		              "too many redirects or authentication replays");
555
		error = GIT_ERROR; /* not GIT_EAUTH because the exact cause is unclear */
556
		goto done;
557 558
	}

559
	GIT_ASSERT(stream->state == HTTP_STATE_SENDING_REQUEST);
560

561
	error = git_http_client_send_body(transport->http_client, buffer, len);
562

563 564 565 566
done:
	git_http_response_dispose(&response);
	git_net_url_dispose(&url);
	return error;
567 568
}

569 570 571 572 573 574 575 576 577
/*
* 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)
578
{
579 580
	http_stream *stream = (http_stream *)s;
	http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
581
	git_http_client *client = transport->http_client;
582 583 584
	git_http_response response = {0};
	bool complete;
	int error;
585

586
	*out_len = 0;
587

588
	if (stream->state == HTTP_STATE_SENDING_REQUEST) {
589
		if ((error = git_http_client_read_response(&response, client)) < 0 ||
590 591
		    (error = handle_response(&complete, stream, &response, false)) < 0)
		    goto done;
592

593
		GIT_ASSERT(complete);
594 595
		stream->state = HTTP_STATE_RECEIVING_RESPONSE;
	}
596

597
	error = git_http_client_read_body(client, buffer, buffer_size);
598

599 600 601 602
	if (error > 0) {
		*out_len = error;
		error = 0;
	}
603

604 605 606
done:
	git_http_response_dispose(&response);
	return error;
607
}
608

609 610
static void http_stream_free(git_smart_subtransport_stream *stream)
{
611
	http_stream *s = GIT_CONTAINER_OF(stream, http_stream, parent);
612 613
	git__free(s);
}
614

615
static const http_service *select_service(git_smart_service_t action)
616
{
617 618 619 620 621 622 623 624 625 626
	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;
	}
627

628
	return NULL;
629 630
}

631
static int http_action(
632 633
	git_smart_subtransport_stream **out,
	git_smart_subtransport *t,
634 635
	const char *url,
	git_smart_service_t action)
636
{
637 638 639 640 641
	http_subtransport *transport = GIT_CONTAINER_OF(t, http_subtransport, parent);
	http_stream *stream;
	const http_service *service;
	int error;

642 643
	GIT_ASSERT_ARG(out);
	GIT_ASSERT_ARG(t);
644

645
	*out = NULL;
646

647 648 649 650 651 652 653
	/*
	 * 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).
	 */
654 655 656
	if (!git_net_url_valid(&transport->server.url) &&
	    (error = git_net_url_parse(&transport->server.url, url)) < 0)
		return error;
657

658
	if ((service = select_service(action)) == NULL) {
659
		git_error_set(GIT_ERROR_HTTP, "invalid action");
660 661
		return -1;
	}
662

663 664
	stream = git__calloc(sizeof(http_stream), 1);
	GIT_ERROR_CHECK_ALLOC(stream);
665

666 667
	if (!transport->http_client) {
		git_http_client_options opts = {0};
668

669 670 671 672
		opts.server_certificate_check_cb = transport->owner->certificate_check_cb;
		opts.server_certificate_check_payload = transport->owner->message_cb_payload;
		opts.proxy_certificate_check_cb = transport->owner->proxy.certificate_check;
		opts.proxy_certificate_check_payload = transport->owner->proxy.payload;
673

674 675
		if (git_http_client_new(&transport->http_client, &opts) < 0)
			return -1;
676
	}
677

678 679
	stream->service = service;
	stream->parent.subtransport = &transport->parent;
680

681 682 683 684 685
	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;
686
	}
687

688
	stream->parent.free = http_stream_free;
689

690 691 692
	*out = (git_smart_subtransport_stream *)stream;
	return 0;
}
693

694 695 696
static int http_close(git_smart_subtransport *t)
{
	http_subtransport *transport = GIT_CONTAINER_OF(t, http_subtransport, parent);
697

698 699
	free_cred(&transport->server.cred);
	free_cred(&transport->proxy.cred);
700

701 702
	transport->server.url_cred_presented = false;
	transport->proxy.url_cred_presented = false;
703

704 705
	git_net_url_dispose(&transport->server.url);
	git_net_url_dispose(&transport->proxy.url);
706

707 708 709
	return 0;
}

710
static void http_free(git_smart_subtransport *t)
711
{
712
	http_subtransport *transport = GIT_CONTAINER_OF(t, http_subtransport, parent);
713

714 715 716 717
	git_http_client_free(transport->http_client);

	http_close(t);
	git__free(transport);
718 719
}

720
int git_smart_subtransport_http(git_smart_subtransport **out, git_transport *owner, void *param)
721
{
722
	http_subtransport *transport;
723

724 725
	GIT_UNUSED(param);

726
	GIT_ASSERT_ARG(out);
727

728 729
	transport = git__calloc(sizeof(http_subtransport), 1);
	GIT_ERROR_CHECK_ALLOC(transport);
730

731 732 733 734
	transport->owner = (transport_smart *)owner;
	transport->parent.action = http_action;
	transport->parent.close = http_close;
	transport->parent.free = http_free;
735

736
	*out = (git_smart_subtransport *) transport;
737 738
	return 0;
}
739 740

#endif /* !GIT_WINHTTP */