File Coverage

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