File Coverage

deps/libgit2/src/transports/http.c
Criterion Covered Total %
statement 0 302 0.0
branch 0 186 0.0
condition n/a
subroutine n/a
pod n/a
total 0 488 0.0


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