1 /*
2  * Copyright (C) the libgit2 contributors. All rights reserved.
3  *
4  * This file is part of libgit2, distributed under the GNU GPL v2 with
5  * a Linking Exception. For full terms see the included COPYING file.
6  */
7 
8 #include "common.h"
9 
10 #ifndef GIT_WINHTTP
11 
12 #include "git2.h"
13 #include "http_parser.h"
14 #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 #include "streams/tls.h"
26 #include "streams/socket.h"
27 #include "httpclient.h"
28 
29 bool git_http__expect_continue = false;
30 
31 typedef enum {
32 	HTTP_STATE_NONE = 0,
33 	HTTP_STATE_SENDING_REQUEST,
34 	HTTP_STATE_RECEIVING_RESPONSE,
35 	HTTP_STATE_DONE
36 } http_state;
37 
38 typedef struct {
39 	git_http_method method;
40 	const char *url;
41 	const char *request_type;
42 	const char *response_type;
43 	unsigned chunked : 1;
44 } http_service;
45 
46 typedef struct {
47 	git_smart_subtransport_stream parent;
48 	const http_service *service;
49 	http_state state;
50 	unsigned replay_count;
51 } http_stream;
52 
53 typedef struct {
54 	git_net_url url;
55 
56 	git_credential *cred;
57 	unsigned auth_schemetypes;
58 	unsigned url_cred_presented : 1;
59 } http_server;
60 
61 typedef struct {
62 	git_smart_subtransport parent;
63 	transport_smart *owner;
64 
65 	http_server server;
66 	http_server proxy;
67 
68 	git_http_client *http_client;
69 } http_subtransport;
70 
71 static const http_service upload_pack_ls_service = {
72 	GIT_HTTP_METHOD_GET, "/info/refs?service=git-upload-pack",
73 	NULL,
74 	"application/x-git-upload-pack-advertisement",
75 	0
76 };
77 static const http_service upload_pack_service = {
78 	GIT_HTTP_METHOD_POST, "/git-upload-pack",
79 	"application/x-git-upload-pack-request",
80 	"application/x-git-upload-pack-result",
81 	0
82 };
83 static const http_service receive_pack_ls_service = {
84 	GIT_HTTP_METHOD_GET, "/info/refs?service=git-receive-pack",
85 	NULL,
86 	"application/x-git-receive-pack-advertisement",
87 	0
88 };
89 static const http_service receive_pack_service = {
90 	GIT_HTTP_METHOD_POST, "/git-receive-pack",
91 	"application/x-git-receive-pack-request",
92 	"application/x-git-receive-pack-result",
93 	1
94 };
95 
96 #define SERVER_TYPE_REMOTE "remote"
97 #define SERVER_TYPE_PROXY  "proxy"
98 
99 #define OWNING_SUBTRANSPORT(s) ((http_subtransport *)(s)->parent.subtransport)
100 
apply_url_credentials(git_credential ** cred,unsigned int allowed_types,const char * username,const char * password)101 static int apply_url_credentials(
102 	git_credential **cred,
103 	unsigned int allowed_types,
104 	const char *username,
105 	const char *password)
106 {
107 	GIT_ASSERT_ARG(username);
108 
109 	if (!password)
110 		password = "";
111 
112 	if (allowed_types & GIT_CREDENTIAL_USERPASS_PLAINTEXT)
113 		return git_credential_userpass_plaintext_new(cred, username, password);
114 
115 	if ((allowed_types & GIT_CREDENTIAL_DEFAULT) && *username == '\0' && *password == '\0')
116 		return git_credential_default_new(cred);
117 
118 	return GIT_PASSTHROUGH;
119 }
120 
free_cred(git_credential ** cred)121 GIT_INLINE(void) free_cred(git_credential **cred)
122 {
123 	if (*cred) {
124 		git_credential_free(*cred);
125 		(*cred) = NULL;
126 	}
127 }
128 
handle_auth(http_server * server,const char * server_type,const char * url,unsigned int allowed_schemetypes,unsigned int allowed_credtypes,git_credential_acquire_cb callback,void * callback_payload)129 static int handle_auth(
130 	http_server *server,
131 	const char *server_type,
132 	const char *url,
133 	unsigned int allowed_schemetypes,
134 	unsigned int allowed_credtypes,
135 	git_credential_acquire_cb callback,
136 	void *callback_payload)
137 {
138 	int error = 1;
139 
140 	if (server->cred)
141 		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 		/* treat GIT_PASSTHROUGH as if callback isn't set */
151 		if (error == GIT_PASSTHROUGH)
152 			error = 1;
153 	}
154 
155 	if (error > 0 && callback) {
156 		error = callback(&server->cred, url, server->url.username, allowed_credtypes, callback_payload);
157 
158 		/* treat GIT_PASSTHROUGH as if callback isn't set */
159 		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 	if (!error)
169 		server->auth_schemetypes = allowed_schemetypes;
170 
171 	return error;
172 }
173 
handle_remote_auth(http_stream * stream,git_http_response * response)174 GIT_INLINE(int) handle_remote_auth(
175 	http_stream *stream,
176 	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 	/* Otherwise, prompt for credentials. */
186 	return handle_auth(
187 		&transport->server,
188 		SERVER_TYPE_REMOTE,
189 		transport->owner->url,
190 		response->server_auth_schemetypes,
191 		response->server_auth_credtypes,
192 		transport->owner->cred_acquire_cb,
193 		transport->owner->cred_acquire_payload);
194 }
195 
handle_proxy_auth(http_stream * stream,git_http_response * response)196 GIT_INLINE(int) handle_proxy_auth(
197 	http_stream *stream,
198 	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 	/* Otherwise, prompt for credentials. */
208 	return handle_auth(
209 		&transport->proxy,
210 		SERVER_TYPE_PROXY,
211 		transport->owner->proxy.url,
212 		response->server_auth_schemetypes,
213 		response->proxy_auth_credtypes,
214 		transport->owner->proxy.credentials,
215 		transport->owner->proxy.payload);
216 }
217 
218 
handle_response(bool * complete,http_stream * stream,git_http_response * response,bool allow_replay)219 static int handle_response(
220 	bool *complete,
221 	http_stream *stream,
222 	git_http_response *response,
223 	bool allow_replay)
224 {
225 	http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
226 	int error;
227 
228 	*complete = false;
229 
230 	if (allow_replay && git_http_response_is_redirect(response)) {
231 		if (!response->location) {
232 			git_error_set(GIT_ERROR_HTTP, "redirect without location");
233 			return -1;
234 		}
235 
236 		if (git_net_url_apply_redirect(&transport->server.url, response->location, stream->service->url) < 0) {
237 			return -1;
238 		}
239 
240 		return 0;
241 	} else if (git_http_response_is_redirect(response)) {
242 		git_error_set(GIT_ERROR_HTTP, "unexpected redirect");
243 		return -1;
244 	}
245 
246 	/* If we're in the middle of challenge/response auth, continue. */
247 	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 			return error;
252 
253 		return git_http_client_skip_body(transport->http_client);
254 	} else if (allow_replay && response->status == GIT_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED) {
255 		if ((error = handle_proxy_auth(stream, response)) < 0)
256 			return error;
257 
258 		return git_http_client_skip_body(transport->http_client);
259 	} else if (response->status == GIT_HTTP_STATUS_UNAUTHORIZED ||
260 	           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 	/* The response must contain a Content-Type header. */
271 	if (!response->content_type) {
272 		git_error_set(GIT_ERROR_HTTP, "no content-type header in response");
273 		return -1;
274 	}
275 
276 	/* The Content-Type header must match our expectation. */
277 	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 	*complete = true;
283 	stream->state = HTTP_STATE_RECEIVING_RESPONSE;
284 	return 0;
285 }
286 
lookup_proxy(bool * out_use,http_subtransport * transport)287 static int lookup_proxy(
288 	bool *out_use,
289 	http_subtransport *transport)
290 {
291 	const char *proxy;
292 	git_remote *remote;
293 	char *config = NULL;
294 	int error = 0;
295 
296 	*out_use = false;
297 	git_net_url_dispose(&transport->proxy.url);
298 
299 	switch (transport->owner->proxy.type) {
300 	case GIT_PROXY_SPECIFIED:
301 		proxy = transport->owner->proxy.url;
302 		break;
303 
304 	case GIT_PROXY_AUTO:
305 		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 		proxy = config;
313 		break;
314 
315 	default:
316 		return 0;
317 	}
318 
319 	if (!proxy ||
320 	    (error = git_net_url_parse(&transport->proxy.url, proxy)) < 0)
321 		goto done;
322 
323 	*out_use = true;
324 
325 done:
326 	git__free(config);
327 	return error;
328 }
329 
generate_request(git_net_url * url,git_http_request * request,http_stream * stream,size_t len)330 static int generate_request(
331 	git_net_url *url,
332 	git_http_request *request,
333 	http_stream *stream,
334 	size_t len)
335 {
336 	http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
337 	bool use_proxy = false;
338 	int error;
339 
340 	if ((error = git_net_url_joinpath(url,
341 		&transport->server.url, stream->service->url)) < 0 ||
342 	    (error = lookup_proxy(&use_proxy, transport)) < 0)
343 		return error;
344 
345 	request->method = stream->service->method;
346 	request->url = url;
347 	request->credentials = transport->server.cred;
348 	request->proxy = use_proxy ? &transport->proxy.url : NULL;
349 	request->proxy_credentials = transport->proxy.cred;
350 	request->custom_headers = &transport->owner->custom_headers;
351 
352 	if (stream->service->method == GIT_HTTP_METHOD_POST) {
353 		request->chunked = stream->service->chunked;
354 		request->content_length = stream->service->chunked ? 0 : len;
355 		request->content_type = stream->service->request_type;
356 		request->accept = stream->service->response_type;
357 		request->expect_continue = git_http__expect_continue;
358 	}
359 
360 	return 0;
361 }
362 
363 /*
364  * Read from an HTTP transport - for the first invocation of this function
365  * (ie, when stream->state == HTTP_STATE_NONE), we'll send a GET request
366  * to the remote host.  We will stream that data back on all subsequent
367  * calls.
368  */
http_stream_read(git_smart_subtransport_stream * s,char * buffer,size_t buffer_size,size_t * out_len)369 static int http_stream_read(
370 	git_smart_subtransport_stream *s,
371 	char *buffer,
372 	size_t buffer_size,
373 	size_t *out_len)
374 {
375 	http_stream *stream = (http_stream *)s;
376 	http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
377 	git_net_url url = GIT_NET_URL_INIT;
378 	git_net_url proxy_url = GIT_NET_URL_INIT;
379 	git_http_request request = {0};
380 	git_http_response response = {0};
381 	bool complete;
382 	int error;
383 
384 	*out_len = 0;
385 
386 	if (stream->state == HTTP_STATE_NONE) {
387 		stream->state = HTTP_STATE_SENDING_REQUEST;
388 		stream->replay_count = 0;
389 	}
390 
391 	/*
392 	 * Formulate the URL, send the request and read the response
393 	 * headers.  Some of the request body may also be read.
394 	 */
395 	while (stream->state == HTTP_STATE_SENDING_REQUEST &&
396 	       stream->replay_count < GIT_HTTP_REPLAY_MAX) {
397 		git_net_url_dispose(&url);
398 		git_net_url_dispose(&proxy_url);
399 		git_http_response_dispose(&response);
400 
401 		if ((error = generate_request(&url, &request, stream, 0)) < 0 ||
402 		    (error = git_http_client_send_request(
403 			transport->http_client, &request)) < 0 ||
404 		    (error = git_http_client_read_response(
405 			    &response, transport->http_client)) < 0 ||
406 		    (error = handle_response(&complete, stream, &response, true)) < 0)
407 			goto done;
408 
409 		if (complete)
410 			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 		goto done;
419 	}
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 	if (error > 0) {
426 		*out_len = error;
427 		error = 0;
428 	}
429 
430 done:
431 	git_net_url_dispose(&url);
432 	git_net_url_dispose(&proxy_url);
433 	git_http_response_dispose(&response);
434 
435 	return error;
436 }
437 
needs_probe(http_stream * stream)438 static bool needs_probe(http_stream *stream)
439 {
440 	http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
441 
442 	return (transport->server.auth_schemetypes == GIT_HTTP_AUTH_NTLM ||
443 	        transport->server.auth_schemetypes == GIT_HTTP_AUTH_NEGOTIATE);
444 }
445 
send_probe(http_stream * stream)446 static int send_probe(http_stream *stream)
447 {
448 	http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
449 	git_http_client *client = transport->http_client;
450 	const char *probe = "0000";
451 	size_t len = 4;
452 	git_net_url url = GIT_NET_URL_INIT;
453 	git_http_request request = {0};
454 	git_http_response response = {0};
455 	bool complete = false;
456 	size_t step, steps = 1;
457 	int error;
458 
459 	/* NTLM requires a full challenge/response */
460 	if (transport->server.auth_schemetypes == GIT_HTTP_AUTH_NTLM)
461 		steps = GIT_AUTH_STEPS_NTLM;
462 
463 	/*
464 	 * Send at most two requests: one without any authentication to see
465 	 * if we get prompted to authenticate.  If we do, send a second one
466 	 * with the first authentication message.  The final authentication
467 	 * message with the response will occur with the *actual* POST data.
468 	 */
469 	for (step = 0; step < steps && !complete; step++) {
470 		git_net_url_dispose(&url);
471 		git_http_response_dispose(&response);
472 
473 		if ((error = generate_request(&url, &request, stream, len)) < 0 ||
474 		    (error = git_http_client_send_request(client, &request)) < 0 ||
475 		    (error = git_http_client_send_body(client, probe, len)) < 0 ||
476 		    (error = git_http_client_read_response(&response, client)) < 0 ||
477 		    (error = git_http_client_skip_body(client)) < 0 ||
478 		    (error = handle_response(&complete, stream, &response, true)) < 0)
479 			goto done;
480 	}
481 
482 done:
483 	git_http_response_dispose(&response);
484 	git_net_url_dispose(&url);
485 	return error;
486 }
487 
488 /*
489 * Write to an HTTP transport - for the first invocation of this function
490 * (ie, when stream->state == HTTP_STATE_NONE), we'll send a POST request
491 * to the remote host.  If we're sending chunked data, then subsequent calls
492 * will write the additional data given in the buffer.  If we're not chunking,
493 * then the caller should have given us all the data in the original call.
494 * The caller should call http_stream_read_response to get the result.
495 */
http_stream_write(git_smart_subtransport_stream * s,const char * buffer,size_t len)496 static int http_stream_write(
497 	git_smart_subtransport_stream *s,
498 	const char *buffer,
499 	size_t len)
500 {
501 	http_stream *stream = GIT_CONTAINER_OF(s, http_stream, parent);
502 	http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
503 	git_net_url url = GIT_NET_URL_INIT;
504 	git_http_request request = {0};
505 	git_http_response response = {0};
506 	int error;
507 
508 	while (stream->state == HTTP_STATE_NONE &&
509 	       stream->replay_count < GIT_HTTP_REPLAY_MAX) {
510 
511 		git_net_url_dispose(&url);
512 		git_http_response_dispose(&response);
513 
514 		/*
515 		 * If we're authenticating with a connection-based mechanism
516 		 * (NTLM, Kerberos), send a "probe" packet.  Servers SHOULD
517 		 * authenticate an entire keep-alive connection, so ideally
518 		 * we should not need to authenticate but some servers do
519 		 * not support this.  By sending a probe packet, we'll be
520 		 * able to follow up with a second POST using the actual
521 		 * data (and, in the degenerate case, the authentication
522 		 * header as well).
523 		 */
524 		if (needs_probe(stream) && (error = send_probe(stream)) < 0)
525 			goto done;
526 
527 		/* Send the regular POST request. */
528 		if ((error = generate_request(&url, &request, stream, len)) < 0 ||
529 		    (error = git_http_client_send_request(
530 			transport->http_client, &request)) < 0)
531 			goto done;
532 
533 		if (request.expect_continue &&
534 		    git_http_client_has_response(transport->http_client)) {
535 			bool complete;
536 
537 			/*
538 			 * If we got a response to an expect/continue, then
539 			 * it's something other than a 100 and we should
540 			 * deal with the response somehow.
541 			 */
542 			if ((error = git_http_client_read_response(&response, transport->http_client)) < 0 ||
543 			    (error = handle_response(&complete, stream, &response, true)) < 0)
544 			    goto done;
545 		} else {
546 			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 done:
564 	git_http_response_dispose(&response);
565 	git_net_url_dispose(&url);
566 	return error;
567 }
568 
569 /*
570 * Read from an HTTP transport after it has been written to.  This is the
571 * response from a POST request made by http_stream_write.
572 */
http_stream_read_response(git_smart_subtransport_stream * s,char * buffer,size_t buffer_size,size_t * out_len)573 static int http_stream_read_response(
574 	git_smart_subtransport_stream *s,
575 	char *buffer,
576 	size_t buffer_size,
577 	size_t *out_len)
578 {
579 	http_stream *stream = (http_stream *)s;
580 	http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
581 	git_http_client *client = transport->http_client;
582 	git_http_response response = {0};
583 	bool complete;
584 	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 		    (error = handle_response(&complete, stream, &response, false)) < 0)
591 		    goto done;
592 
593 		GIT_ASSERT(complete);
594 		stream->state = HTTP_STATE_RECEIVING_RESPONSE;
595 	}
596 
597 	error = git_http_client_read_body(client, buffer, buffer_size);
598 
599 	if (error > 0) {
600 		*out_len = error;
601 		error = 0;
602 	}
603 
604 done:
605 	git_http_response_dispose(&response);
606 	return error;
607 }
608 
http_stream_free(git_smart_subtransport_stream * stream)609 static void http_stream_free(git_smart_subtransport_stream *stream)
610 {
611 	http_stream *s = GIT_CONTAINER_OF(stream, http_stream, parent);
612 	git__free(s);
613 }
614 
select_service(git_smart_service_t action)615 static const http_service *select_service(git_smart_service_t action)
616 {
617 	switch (action) {
618 	case GIT_SERVICE_UPLOADPACK_LS:
619 		return &upload_pack_ls_service;
620 	case GIT_SERVICE_UPLOADPACK:
621 		return &upload_pack_service;
622 	case GIT_SERVICE_RECEIVEPACK_LS:
623 		return &receive_pack_ls_service;
624 	case GIT_SERVICE_RECEIVEPACK:
625 		return &receive_pack_service;
626 	}
627 
628 	return NULL;
629 }
630 
http_action(git_smart_subtransport_stream ** out,git_smart_subtransport * t,const char * url,git_smart_service_t action)631 static int http_action(
632 	git_smart_subtransport_stream **out,
633 	git_smart_subtransport *t,
634 	const char *url,
635 	git_smart_service_t action)
636 {
637 	http_subtransport *transport = GIT_CONTAINER_OF(t, http_subtransport, parent);
638 	http_stream *stream;
639 	const http_service *service;
640 	int error;
641 
642 	GIT_ASSERT_ARG(out);
643 	GIT_ASSERT_ARG(t);
644 
645 	*out = NULL;
646 
647 	/*
648 	 * If we've seen a redirect then preserve the location that we've
649 	 * been given.  This is important to continue authorization against
650 	 * the redirect target, not the user-given source; the endpoint may
651 	 * have redirected us from HTTP->HTTPS and is using an auth mechanism
652 	 * that would be insecure in plaintext (eg, HTTP Basic).
653 	 */
654 	if (!git_net_url_valid(&transport->server.url) &&
655 	    (error = git_net_url_parse(&transport->server.url, url)) < 0)
656 		return error;
657 
658 	if ((service = select_service(action)) == NULL) {
659 		git_error_set(GIT_ERROR_HTTP, "invalid action");
660 		return -1;
661 	}
662 
663 	stream = git__calloc(sizeof(http_stream), 1);
664 	GIT_ERROR_CHECK_ALLOC(stream);
665 
666 	if (!transport->http_client) {
667 		git_http_client_options opts = {0};
668 
669 		opts.server_certificate_check_cb = transport->owner->certificate_check_cb;
670 		opts.server_certificate_check_payload = transport->owner->message_cb_payload;
671 		opts.proxy_certificate_check_cb = transport->owner->proxy.certificate_check;
672 		opts.proxy_certificate_check_payload = transport->owner->proxy.payload;
673 
674 		if (git_http_client_new(&transport->http_client, &opts) < 0)
675 			return -1;
676 	}
677 
678 	stream->service = service;
679 	stream->parent.subtransport = &transport->parent;
680 
681 	if (service->method == GIT_HTTP_METHOD_GET) {
682 		stream->parent.read = http_stream_read;
683 	} else {
684 		stream->parent.write = http_stream_write;
685 		stream->parent.read = http_stream_read_response;
686 	}
687 
688 	stream->parent.free = http_stream_free;
689 
690 	*out = (git_smart_subtransport_stream *)stream;
691 	return 0;
692 }
693 
http_close(git_smart_subtransport * t)694 static int http_close(git_smart_subtransport *t)
695 {
696 	http_subtransport *transport = GIT_CONTAINER_OF(t, http_subtransport, parent);
697 
698 	free_cred(&transport->server.cred);
699 	free_cred(&transport->proxy.cred);
700 
701 	transport->server.url_cred_presented = false;
702 	transport->proxy.url_cred_presented = false;
703 
704 	git_net_url_dispose(&transport->server.url);
705 	git_net_url_dispose(&transport->proxy.url);
706 
707 	return 0;
708 }
709 
http_free(git_smart_subtransport * t)710 static void http_free(git_smart_subtransport *t)
711 {
712 	http_subtransport *transport = GIT_CONTAINER_OF(t, http_subtransport, parent);
713 
714 	git_http_client_free(transport->http_client);
715 
716 	http_close(t);
717 	git__free(transport);
718 }
719 
git_smart_subtransport_http(git_smart_subtransport ** out,git_transport * owner,void * param)720 int git_smart_subtransport_http(git_smart_subtransport **out, git_transport *owner, void *param)
721 {
722 	http_subtransport *transport;
723 
724 	GIT_UNUSED(param);
725 
726 	GIT_ASSERT_ARG(out);
727 
728 	transport = git__calloc(sizeof(http_subtransport), 1);
729 	GIT_ERROR_CHECK_ALLOC(transport);
730 
731 	transport->owner = (transport_smart *)owner;
732 	transport->parent.action = http_action;
733 	transport->parent.close = http_close;
734 	transport->parent.free = http_free;
735 
736 	*out = (git_smart_subtransport *) transport;
737 	return 0;
738 }
739 
740 #endif /* !GIT_WINHTTP */
741