Line Flow Count Block(s) Source
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 1 2 static int apply_url_credentials(
103 - git_credential **cred,
104 - unsigned int allowed_types,
105 - const char *username,
106 - const char *password)
107 - {
108 1 2 if (allowed_types & GIT_CREDENTIAL_USERPASS_PLAINTEXT)
109 1 3 return git_credential_userpass_plaintext_new(cred, username, password);
110 -
111 ##### 4-6 if ((allowed_types & GIT_CREDENTIAL_DEFAULT) && *username == '\0' && *password == '\0')
112 ##### 7 return git_credential_default_new(cred);
113 -
114 ##### 8 return GIT_PASSTHROUGH;
115 - }
116 -
117 328 2 GIT_INLINE(void) free_cred(git_credential **cred)
118 - {
119 328 2 if (*cred) {
120 6 3 git_credential_free(*cred);
121 6 4 (*cred) = NULL;
122 - }
123 328 5 }
124 -
125 10 2 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 10 2 int error = 1;
135 -
136 10 2 if (server->cred)
137 4 3 free_cred(&server->cred);
138 -
139 - /* Start with URL-specified credentials, if there were any. */
140 10 4,5 if ((allowed_credtypes & GIT_CREDENTIAL_USERPASS_PLAINTEXT) &&
141 10 5,6 !server->url_cred_presented &&
142 10 6,7 server->url.username &&
143 2 7 server->url.password) {
144 1 8 error = apply_url_credentials(&server->cred, allowed_credtypes, server->url.username, server->url.password);
145 1 9 server->url_cred_presented = 1;
146 -
147 - /* treat GIT_PASSTHROUGH as if callback isn't set */
148 1 9 if (error == GIT_PASSTHROUGH)
149 ##### 10 error = 1;
150 - }
151 -
152 10 11,12 if (error > 0 && callback) {
153 9 13 error = callback(&server->cred, url, server->url.username, allowed_credtypes, callback_payload);
154 -
155 - /* treat GIT_PASSTHROUGH as if callback isn't set */
156 9 14 if (error == GIT_PASSTHROUGH)
157 ##### 15 error = 1;
158 - }
159 -
160 10 16 if (error > 0) {
161 ##### 17 git_error_set(GIT_ERROR_HTTP, "%s authentication required but no callback set", server_type);
162 ##### 18 error = -1;
163 - }
164 -
165 10 19 if (!error)
166 6 20 server->auth_schemetypes = allowed_schemetypes;
167 -
168 10 21 return error;
169 - }
170 -
171 10 2 GIT_INLINE(int) handle_remote_auth(
172 - http_stream *stream,
173 - git_http_response *response)
174 - {
175 10 2 http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
176 -
177 10 2 if (response->server_auth_credtypes == 0) {
178 ##### 3 git_error_set(GIT_ERROR_HTTP, "server requires authentication that we do not support");
179 ##### 4 return -1;
180 - }
181 -
182 - /* Otherwise, prompt for credentials. */
183 10 5,5,5 return handle_auth(
184 - &transport->server,
185 - SERVER_TYPE_REMOTE,
186 10 5 transport->owner->url,
187 - response->server_auth_schemetypes,
188 - response->server_auth_credtypes,
189 10 5 transport->owner->cred_acquire_cb,
190 10 5 transport->owner->cred_acquire_payload);
191 - }
192 -
193 ##### 2 GIT_INLINE(int) handle_proxy_auth(
194 - http_stream *stream,
195 - git_http_response *response)
196 - {
197 ##### 2 http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
198 -
199 ##### 2 if (response->proxy_auth_credtypes == 0) {
200 ##### 3 git_error_set(GIT_ERROR_HTTP, "proxy requires authentication that we do not support");
201 ##### 4 return -1;
202 - }
203 -
204 - /* Otherwise, prompt for credentials. */
205 ##### 5,5,5 return handle_auth(
206 - &transport->proxy,
207 - SERVER_TYPE_PROXY,
208 ##### 5 transport->owner->proxy.url,
209 - response->server_auth_schemetypes,
210 - response->proxy_auth_credtypes,
211 ##### 5 transport->owner->proxy.credentials,
212 ##### 5 transport->owner->proxy.payload);
213 - }
214 -
215 -
216 85 2 static int handle_response(
217 - bool *complete,
218 - http_stream *stream,
219 - git_http_response *response,
220 - bool allow_replay)
221 - {
222 85 2 http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
223 - int error;
224 -
225 85 2 *complete = false;
226 -
227 85 2-4 if (allow_replay && git_http_response_is_redirect(response)) {
228 20 5 if (!response->location) {
229 ##### 6 git_error_set(GIT_ERROR_HTTP, "redirect without location");
230 ##### 7 return -1;
231 - }
232 -
233 20 8,9 if (git_net_url_apply_redirect(&transport->server.url, response->location, stream->service->url) < 0) {
234 ##### 10 return -1;
235 - }
236 -
237 20 11 return 0;
238 65 12,13 } else if (git_http_response_is_redirect(response)) {
239 ##### 14 git_error_set(GIT_ERROR_HTTP, "unexpected redirect");
240 ##### 15 return -1;
241 - }
242 -
243 - /* If we're in the middle of challenge/response auth, continue. */
244 65 16,17 if (allow_replay && response->resend_credentials) {
245 ##### 18 return 0;
246 65 19,20 } else if (allow_replay && response->status == GIT_HTTP_STATUS_UNAUTHORIZED) {
247 10 21,22 if ((error = handle_remote_auth(stream, response)) < 0)
248 4 23 return error;
249 -
250 6 24 return git_http_client_skip_body(transport->http_client);
251 55 25,26 } else if (allow_replay && response->status == GIT_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED) {
252 ##### 27,28 if ((error = handle_proxy_auth(stream, response)) < 0)
253 ##### 29 return error;
254 -
255 ##### 30 return git_http_client_skip_body(transport->http_client);
256 55 31,32 } else if (response->status == GIT_HTTP_STATUS_UNAUTHORIZED ||
257 55 32 response->status == GIT_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED) {
258 ##### 33 git_error_set(GIT_ERROR_HTTP, "unexpected authentication failure");
259 ##### 34 return -1;
260 - }
261 -
262 55 35 if (response->status != GIT_HTTP_STATUS_OK) {
263 ##### 36 git_error_set(GIT_ERROR_HTTP, "unexpected http status code: %d", response->status);
264 ##### 37 return -1;
265 - }
266 -
267 - /* The response must contain a Content-Type header. */
268 55 38 if (!response->content_type) {
269 ##### 39 git_error_set(GIT_ERROR_HTTP, "no content-type header in response");
270 ##### 40 return -1;
271 - }
272 -
273 - /* The Content-Type header must match our expectation. */
274 55 41 if (strcmp(response->content_type, stream->service->response_type) != 0) {
275 ##### 42 git_error_set(GIT_ERROR_HTTP, "invalid content-type: '%s'", response->content_type);
276 ##### 43 return -1;
277 - }
278 -
279 55 44 *complete = true;
280 55 44 stream->state = HTTP_STATE_RECEIVING_RESPONSE;
281 55 44 return 0;
282 - }
283 -
284 92 2 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 92 2 char *config = NULL;
292 92 2 int error = 0;
293 -
294 92 2 *out_use = false;
295 92 2 git_net_url_dispose(&transport->proxy.url);
296 -
297 92 3 switch (transport->owner->proxy.type) {
298 - case GIT_PROXY_SPECIFIED:
299 ##### 4 proxy = transport->owner->proxy.url;
300 ##### 4 break;
301 -
302 - case GIT_PROXY_AUTO:
303 6 5 remote = transport->owner->owner;
304 6 5 use_ssl = !strcmp(transport->server.url.scheme, "https");
305 -
306 6 5 error = git_remote__get_http_proxy(remote, use_ssl, &config);
307 -
308 6 6,7 if (error || !config)
309 - goto done;
310 -
311 ##### 8 proxy = config;
312 ##### 8 break;
313 -
314 - default:
315 86 9 return 0;
316 - }
317 -
318 ##### 10-12 if (!proxy ||
319 ##### 11 (error = git_net_url_parse(&transport->proxy.url, proxy)) < 0)
320 - goto done;
321 -
322 ##### 13 *out_use = true;
323 -
324 - done:
325 6 14 git__free(config);
326 6 15 return error;
327 - }
328 -
329 92 2 static int generate_request(
330 - git_net_url *url,
331 - git_http_request *request,
332 - http_stream *stream,
333 - size_t len)
334 - {
335 92 2 http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
336 92 2 bool use_proxy = false;
337 - int error;
338 -
339 92 2,3 if ((error = git_net_url_joinpath(url,
340 92 2,4,5 &transport->server.url, stream->service->url)) < 0 ||
341 - (error = lookup_proxy(&use_proxy, transport)) < 0)
342 ##### 6 return error;
343 -
344 92 7 request->method = stream->service->method;
345 92 7 request->url = url;
346 92 7 request->credentials = transport->server.cred;
347 92 7-9 request->proxy = use_proxy ? &transport->proxy.url : NULL;
348 92 10 request->proxy_credentials = transport->proxy.cred;
349 92 10 request->custom_headers = &transport->owner->custom_headers;
350 -
351 92 10 if (stream->service->method == GIT_HTTP_METHOD_POST) {
352 25 11 request->chunked = stream->service->chunked;
353 25 11-13 request->content_length = stream->service->chunked ? 0 : len;
354 25 14 request->content_type = stream->service->request_type;
355 25 14 request->accept = stream->service->response_type;
356 25 14 request->expect_continue = git_http__expect_continue;
357 - }
358 -
359 92 15 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 97 2 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 97 2 http_stream *stream = (http_stream *)s;
375 97 2 http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
376 97 2 git_net_url url = GIT_NET_URL_INIT;
377 97 2 git_net_url proxy_url = GIT_NET_URL_INIT;
378 97 2 git_http_request request = {0};
379 97 2 git_http_response response = {0};
380 - bool complete;
381 - int error;
382 -
383 97 2 *out_len = 0;
384 -
385 97 2 if (stream->state == HTTP_STATE_NONE) {
386 41 3 stream->state = HTTP_STATE_SENDING_REQUEST;
387 41 3 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 123 4,19,20 while (stream->state == HTTP_STATE_SENDING_REQUEST &&
395 67 20 stream->replay_count < GIT_HTTP_REPLAY_MAX) {
396 67 5 git_net_url_dispose(&url);
397 67 6 git_net_url_dispose(&proxy_url);
398 67 7 git_http_response_dispose(&response);
399 -
400 67 8-11 if ((error = generate_request(&url, &request, stream, 0)) < 0 ||
401 67 10 (error = git_http_client_send_request(
402 60 12,13 transport->http_client, &request)) < 0 ||
403 60 12 (error = git_http_client_read_response(
404 60 14,15 &response, transport->http_client)) < 0 ||
405 - (error = handle_response(&complete, stream, &response, true)) < 0)
406 - goto done;
407 -
408 56 16 if (complete)
409 30 17 break;
410 -
411 26 18 stream->replay_count++;
412 - }
413 -
414 86 21 if (stream->state == HTTP_STATE_SENDING_REQUEST) {
415 ##### 22 git_error_set(GIT_ERROR_HTTP, "too many redirects or authentication replays");
416 ##### 23 error = -1;
417 ##### 23 goto done;
418 - }
419 -
420 86 24,25 assert (stream->state == HTTP_STATE_RECEIVING_RESPONSE);
421 -
422 86 26 error = git_http_client_read_body(transport->http_client, buffer, buffer_size);
423 -
424 86 27 if (error > 0) {
425 86 28 *out_len = error;
426 86 28 error = 0;
427 - }
428 -
429 - done:
430 97 29 git_net_url_dispose(&url);
431 97 30 git_net_url_dispose(&proxy_url);
432 97 31 git_http_response_dispose(&response);
433 -
434 97 32 return error;
435 - }
436 -
437 25 2 static bool needs_probe(http_stream *stream)
438 - {
439 25 2 http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
440 -
441 25 2,3 return (transport->server.auth_schemetypes == GIT_HTTP_AUTH_NTLM ||
442 25 3 transport->server.auth_schemetypes == GIT_HTTP_AUTH_NEGOTIATE);
443 - }
444 -
445 ##### 2 static int send_probe(http_stream *stream)
446 - {
447 ##### 2 http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
448 ##### 2 git_http_client *client = transport->http_client;
449 ##### 2 const char *probe = "0000";
450 ##### 2 size_t len = 4;
451 ##### 2 git_net_url url = GIT_NET_URL_INIT;
452 ##### 2 git_http_request request = {0};
453 ##### 2 git_http_response response = {0};
454 ##### 2 bool complete = false;
455 ##### 2 size_t step, steps = 1;
456 - int error;
457 -
458 - /* NTLM requires a full challenge/response */
459 ##### 2 if (transport->server.auth_schemetypes == GIT_HTTP_AUTH_NTLM)
460 ##### 3 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 ##### 4,19-21 for (step = 0; step < steps && !complete; step++) {
469 ##### 5 git_net_url_dispose(&url);
470 ##### 6 git_http_response_dispose(&response);
471 -
472 ##### 7-10 if ((error = generate_request(&url, &request, stream, len)) < 0 ||
473 ##### 11,12 (error = git_http_client_send_request(client, &request)) < 0 ||
474 ##### 13,14 (error = git_http_client_send_body(client, probe, len)) < 0 ||
475 ##### 15,16 (error = git_http_client_read_response(&response, client)) < 0 ||
476 ##### 17,18 (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 ##### 22 git_http_response_dispose(&response);
483 ##### 23 git_net_url_dispose(&url);
484 ##### 24 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 25 2 static int http_stream_write(
496 - git_smart_subtransport_stream *s,
497 - const char *buffer,
498 - size_t len)
499 - {
500 25 2 http_stream *stream = GIT_CONTAINER_OF(s, http_stream, parent);
501 25 2 http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
502 25 2 git_net_url url = GIT_NET_URL_INIT;
503 25 2 git_http_request request = {0};
504 25 2 git_http_response response = {0};
505 - int error;
506 -
507 50 2,25,26 while (stream->state == HTTP_STATE_NONE &&
508 25 26 stream->replay_count < GIT_HTTP_REPLAY_MAX) {
509 -
510 25 3 git_net_url_dispose(&url);
511 25 4 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 25 5-8 if (needs_probe(stream) && (error = send_probe(stream)) < 0)
524 ##### 9 goto done;
525 -
526 - /* Send the regular POST request. */
527 25 10-13 if ((error = generate_request(&url, &request, stream, len)) < 0 ||
528 25 12 (error = git_http_client_send_request(
529 - transport->http_client, &request)) < 0)
530 - goto done;
531 -
532 25 14,16 if (request.expect_continue &&
533 ##### 15,21,22 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 ##### 17-20 if ((error = git_http_client_read_response(&response, transport->http_client)) < 0 ||
542 - (error = handle_response(&complete, stream, &response, true)) < 0)
543 - goto done;
544 - } else {
545 25 23 stream->state = HTTP_STATE_SENDING_REQUEST;
546 - }
547 -
548 25 24 stream->replay_count++;
549 - }
550 -
551 25 27 if (stream->state == HTTP_STATE_NONE) {
552 ##### 28 git_error_set(GIT_ERROR_HTTP,
553 - "too many redirects or authentication replays");
554 ##### 29 error = -1;
555 ##### 29 goto done;
556 - }
557 -
558 25 30,31 assert(stream->state == HTTP_STATE_SENDING_REQUEST);
559 -
560 25 32 error = git_http_client_send_body(transport->http_client, buffer, len);
561 -
562 - done:
563 25 33 git_http_response_dispose(&response);
564 25 34 git_net_url_dispose(&url);
565 25 35 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 445 2 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 445 2 http_stream *stream = (http_stream *)s;
579 445 2 http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
580 445 2 git_http_client *client = transport->http_client;
581 445 2 git_http_response response = {0};
582 - bool complete;
583 - int error;
584 -
585 445 2 *out_len = 0;
586 -
587 445 2 if (stream->state == HTTP_STATE_SENDING_REQUEST) {
588 25 3-6 if ((error = git_http_client_read_response(&response, client)) < 0 ||
589 - (error = handle_response(&complete, stream, &response, false)) < 0)
590 - goto done;
591 -
592 25 7,8 assert(complete);
593 25 9 stream->state = HTTP_STATE_RECEIVING_RESPONSE;
594 - }
595 -
596 445 10 error = git_http_client_read_body(client, buffer, buffer_size);
597 -
598 445 11 if (error > 0) {
599 445 12 *out_len = error;
600 445 12 error = 0;
601 - }
602 -
603 - done:
604 445 13 git_http_response_dispose(&response);
605 445 14 return error;
606 - }
607 -
608 66 2 static void http_stream_free(git_smart_subtransport_stream *stream)
609 - {
610 66 2 http_stream *s = GIT_CONTAINER_OF(stream, http_stream, parent);
611 66 2 git__free(s);
612 66 3 }
613 -
614 66 2 static const http_service *select_service(git_smart_service_t action)
615 - {
616 66 2 switch (action) {
617 - case GIT_SERVICE_UPLOADPACK_LS:
618 41 3 return &upload_pack_ls_service;
619 - case GIT_SERVICE_UPLOADPACK:
620 25 4 return &upload_pack_service;
621 - case GIT_SERVICE_RECEIVEPACK_LS:
622 ##### 5 return &receive_pack_ls_service;
623 - case GIT_SERVICE_RECEIVEPACK:
624 ##### 6 return &receive_pack_service;
625 - }
626 -
627 ##### 7 return NULL;
628 - }
629 -
630 67 2 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 67 2 http_subtransport *transport = GIT_CONTAINER_OF(t, http_subtransport, parent);
637 - http_stream *stream;
638 - const http_service *service;
639 - int error;
640 -
641 67 2-4 assert(out && t);
642 -
643 67 5 *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 67 5-8 if (!git_net_url_valid(&transport->server.url) &&
653 42 7 (error = git_net_url_parse(&transport->server.url, url)) < 0)
654 1 9 return error;
655 -
656 66 10,11 if ((service = select_service(action)) == NULL) {
657 ##### 12 git_error_set(GIT_ERROR_HTTP, "invalid action");
658 ##### 13 return -1;
659 - }
660 -
661 66 14 stream = git__calloc(sizeof(http_stream), 1);
662 66 15,16 GIT_ERROR_CHECK_ALLOC(stream);
663 -
664 66 17 if (!transport->http_client) {
665 40 18 git_http_client_options opts = {0};
666 -
667 40 18 opts.server_certificate_check_cb = transport->owner->certificate_check_cb;
668 40 18 opts.server_certificate_check_payload = transport->owner->message_cb_payload;
669 40 18 opts.proxy_certificate_check_cb = transport->owner->proxy.certificate_check;
670 40 18 opts.proxy_certificate_check_payload = transport->owner->proxy.payload;
671 -
672 40 18,19 if (git_http_client_new(&transport->http_client, &opts) < 0)
673 40 20,21 return -1;
674 - }
675 -
676 66 22 stream->service = service;
677 66 22 stream->parent.subtransport = &transport->parent;
678 -
679 66 22 if (service->method == GIT_HTTP_METHOD_GET) {
680 41 23 stream->parent.read = http_stream_read;
681 - } else {
682 25 24 stream->parent.write = http_stream_write;
683 25 24 stream->parent.read = http_stream_read_response;
684 - }
685 -
686 66 25 stream->parent.free = http_stream_free;
687 -
688 66 25 *out = (git_smart_subtransport_stream *)stream;
689 66 25 return 0;
690 - }
691 -
692 162 2 static int http_close(git_smart_subtransport *t)
693 - {
694 162 2 http_subtransport *transport = GIT_CONTAINER_OF(t, http_subtransport, parent);
695 -
696 162 2 free_cred(&transport->server.cred);
697 162 3 free_cred(&transport->proxy.cred);
698 -
699 162 4 transport->server.url_cred_presented = false;
700 162 4 transport->proxy.url_cred_presented = false;
701 -
702 162 4 git_net_url_dispose(&transport->server.url);
703 162 5 git_net_url_dispose(&transport->proxy.url);
704 -
705 162 6 return 0;
706 - }
707 -
708 45 2 static void http_free(git_smart_subtransport *t)
709 - {
710 45 2 http_subtransport *transport = GIT_CONTAINER_OF(t, http_subtransport, parent);
711 -
712 45 2 git_http_client_free(transport->http_client);
713 -
714 45 3 http_close(t);
715 45 4 git__free(transport);
716 45 5 }
717 -
718 45 2 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 45 2,3 assert(out);
725 -
726 45 4 transport = git__calloc(sizeof(http_subtransport), 1);
727 45 5,6 GIT_ERROR_CHECK_ALLOC(transport);
728 -
729 45 7 transport->owner = (transport_smart *)owner;
730 45 7 transport->parent.action = http_action;
731 45 7 transport->parent.close = http_close;
732 45 7 transport->parent.free = http_free;
733 -
734 45 7 *out = (git_smart_subtransport *) transport;
735 45 7 return 0;
736 - }
737 -
738 - #endif /* !GIT_WINHTTP */