File Coverage

deps/libgit2/src/libgit2/transports/httpclient.c
Criterion Covered Total %
statement 0 703 0.0
branch 0 492 0.0
condition n/a
subroutine n/a
pod n/a
total 0 1195 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             #include "git2.h"
10             #include "http_parser.h"
11             #include "vector.h"
12             #include "trace.h"
13             #include "httpclient.h"
14             #include "http.h"
15             #include "auth.h"
16             #include "auth_negotiate.h"
17             #include "auth_ntlm.h"
18             #include "git2/sys/credential.h"
19             #include "net.h"
20             #include "stream.h"
21             #include "streams/socket.h"
22             #include "streams/tls.h"
23             #include "auth.h"
24              
25             static git_http_auth_scheme auth_schemes[] = {
26             { GIT_HTTP_AUTH_NEGOTIATE, "Negotiate", GIT_CREDENTIAL_DEFAULT, git_http_auth_negotiate },
27             { GIT_HTTP_AUTH_NTLM, "NTLM", GIT_CREDENTIAL_USERPASS_PLAINTEXT, git_http_auth_ntlm },
28             { GIT_HTTP_AUTH_BASIC, "Basic", GIT_CREDENTIAL_USERPASS_PLAINTEXT, git_http_auth_basic },
29             };
30              
31             /*
32             * Use a 16kb read buffer to match the maximum size of a TLS packet. This
33             * is critical for compatibility with SecureTransport, which will always do
34             * a network read on every call, even if it has data buffered to return to
35             * you. That buffered data may be the _end_ of a keep-alive response, so
36             * if SecureTransport performs another network read, it will wait until the
37             * server ultimately times out before it returns that buffered data to you.
38             * Since SecureTransport only reads a single TLS packet at a time, by
39             * calling it with a read buffer that is the maximum size of a TLS packet,
40             * we ensure that it will never buffer.
41             */
42             #define GIT_READ_BUFFER_SIZE (16 * 1024)
43              
44             typedef struct {
45             git_net_url url;
46             git_stream *stream;
47              
48             git_vector auth_challenges;
49             git_http_auth_context *auth_context;
50             } git_http_server;
51              
52             typedef enum {
53             PROXY = 1,
54             SERVER
55             } git_http_server_t;
56              
57             typedef enum {
58             NONE = 0,
59             SENDING_REQUEST,
60             SENDING_BODY,
61             SENT_REQUEST,
62             HAS_EARLY_RESPONSE,
63             READING_RESPONSE,
64             READING_BODY,
65             DONE
66             } http_client_state;
67              
68             /* Parser state */
69             typedef enum {
70             PARSE_HEADER_NONE = 0,
71             PARSE_HEADER_NAME,
72             PARSE_HEADER_VALUE,
73             PARSE_HEADER_COMPLETE
74             } parse_header_state;
75              
76             typedef enum {
77             PARSE_STATUS_OK,
78             PARSE_STATUS_NO_OUTPUT,
79             PARSE_STATUS_ERROR
80             } parse_status;
81              
82             typedef struct {
83             git_http_client *client;
84             git_http_response *response;
85              
86             /* Temporary buffers to avoid extra mallocs */
87             git_str parse_header_name;
88             git_str parse_header_value;
89              
90             /* Parser state */
91             int error;
92             parse_status parse_status;
93              
94             /* Headers parsing */
95             parse_header_state parse_header_state;
96              
97             /* Body parsing */
98             char *output_buf; /* Caller's output buffer */
99             size_t output_size; /* Size of caller's output buffer */
100             size_t output_written; /* Bytes we've written to output buffer */
101             } http_parser_context;
102              
103             /* HTTP client connection */
104             struct git_http_client {
105             git_http_client_options opts;
106              
107             /* Are we writing to the proxy or server, and state of the client. */
108             git_http_server_t current_server;
109             http_client_state state;
110              
111             http_parser parser;
112              
113             git_http_server server;
114             git_http_server proxy;
115              
116             unsigned request_count;
117             unsigned connected : 1,
118             proxy_connected : 1,
119             keepalive : 1,
120             request_chunked : 1;
121              
122             /* Temporary buffers to avoid extra mallocs */
123             git_str request_msg;
124             git_str read_buf;
125              
126             /* A subset of information from the request */
127             size_t request_body_len,
128             request_body_remain;
129              
130             /*
131             * When state == HAS_EARLY_RESPONSE, the response of our proxy
132             * that we have buffered and will deliver during read_response.
133             */
134             git_http_response early_response;
135             };
136              
137 0           bool git_http_response_is_redirect(git_http_response *response)
138             {
139 0 0         return (response->status == GIT_HTTP_MOVED_PERMANENTLY ||
140 0 0         response->status == GIT_HTTP_FOUND ||
141 0 0         response->status == GIT_HTTP_SEE_OTHER ||
142 0 0         response->status == GIT_HTTP_TEMPORARY_REDIRECT ||
    0          
143 0           response->status == GIT_HTTP_PERMANENT_REDIRECT);
144             }
145              
146 0           void git_http_response_dispose(git_http_response *response)
147             {
148 0 0         if (!response)
149 0           return;
150              
151 0           git__free(response->content_type);
152 0           git__free(response->location);
153              
154 0           memset(response, 0, sizeof(git_http_response));
155             }
156              
157 0           static int on_header_complete(http_parser *parser)
158             {
159 0           http_parser_context *ctx = (http_parser_context *) parser->data;
160 0           git_http_client *client = ctx->client;
161 0           git_http_response *response = ctx->response;
162              
163 0           git_str *name = &ctx->parse_header_name;
164 0           git_str *value = &ctx->parse_header_value;
165              
166 0 0         if (!strcasecmp("Content-Type", name->ptr)) {
167 0 0         if (response->content_type) {
168 0           git_error_set(GIT_ERROR_HTTP,
169             "multiple content-type headers");
170 0           return -1;
171             }
172              
173 0           response->content_type =
174 0           git__strndup(value->ptr, value->size);
175 0 0         GIT_ERROR_CHECK_ALLOC(ctx->response->content_type);
176 0 0         } else if (!strcasecmp("Content-Length", name->ptr)) {
177             int64_t len;
178              
179 0 0         if (response->content_length) {
180 0           git_error_set(GIT_ERROR_HTTP,
181             "multiple content-length headers");
182 0           return -1;
183             }
184              
185 0 0         if (git__strntol64(&len, value->ptr, value->size,
186 0 0         NULL, 10) < 0 || len < 0) {
187 0           git_error_set(GIT_ERROR_HTTP,
188             "invalid content-length");
189 0           return -1;
190             }
191              
192 0           response->content_length = (size_t)len;
193 0 0         } else if (!strcasecmp("Transfer-Encoding", name->ptr) &&
    0          
194 0           !strcasecmp("chunked", value->ptr)) {
195 0           ctx->response->chunked = 1;
196 0 0         } else if (!strcasecmp("Proxy-Authenticate", git_str_cstr(name))) {
197 0           char *dup = git__strndup(value->ptr, value->size);
198 0 0         GIT_ERROR_CHECK_ALLOC(dup);
199              
200 0 0         if (git_vector_insert(&client->proxy.auth_challenges, dup) < 0)
201 0           return -1;
202 0 0         } else if (!strcasecmp("WWW-Authenticate", name->ptr)) {
203 0           char *dup = git__strndup(value->ptr, value->size);
204 0 0         GIT_ERROR_CHECK_ALLOC(dup);
205              
206 0 0         if (git_vector_insert(&client->server.auth_challenges, dup) < 0)
207 0           return -1;
208 0 0         } else if (!strcasecmp("Location", name->ptr)) {
209 0 0         if (response->location) {
210 0           git_error_set(GIT_ERROR_HTTP,
211             "multiple location headers");
212 0           return -1;
213             }
214              
215 0           response->location = git__strndup(value->ptr, value->size);
216 0 0         GIT_ERROR_CHECK_ALLOC(response->location);
217             }
218              
219 0           return 0;
220             }
221              
222 0           static int on_header_field(http_parser *parser, const char *str, size_t len)
223             {
224 0           http_parser_context *ctx = (http_parser_context *) parser->data;
225              
226 0           switch (ctx->parse_header_state) {
227             /*
228             * We last saw a header value, process the name/value pair and
229             * get ready to handle this new name.
230             */
231             case PARSE_HEADER_VALUE:
232 0 0         if (on_header_complete(parser) < 0)
233 0           return ctx->parse_status = PARSE_STATUS_ERROR;
234              
235 0           git_str_clear(&ctx->parse_header_name);
236 0           git_str_clear(&ctx->parse_header_value);
237             /* Fall through */
238              
239             case PARSE_HEADER_NONE:
240             case PARSE_HEADER_NAME:
241 0           ctx->parse_header_state = PARSE_HEADER_NAME;
242              
243 0 0         if (git_str_put(&ctx->parse_header_name, str, len) < 0)
244 0           return ctx->parse_status = PARSE_STATUS_ERROR;
245              
246 0           break;
247              
248             default:
249 0           git_error_set(GIT_ERROR_HTTP,
250             "header name seen at unexpected time");
251 0           return ctx->parse_status = PARSE_STATUS_ERROR;
252             }
253              
254 0           return 0;
255             }
256              
257 0           static int on_header_value(http_parser *parser, const char *str, size_t len)
258             {
259 0           http_parser_context *ctx = (http_parser_context *) parser->data;
260              
261 0 0         switch (ctx->parse_header_state) {
262             case PARSE_HEADER_NAME:
263             case PARSE_HEADER_VALUE:
264 0           ctx->parse_header_state = PARSE_HEADER_VALUE;
265              
266 0 0         if (git_str_put(&ctx->parse_header_value, str, len) < 0)
267 0           return ctx->parse_status = PARSE_STATUS_ERROR;
268              
269 0           break;
270              
271             default:
272 0           git_error_set(GIT_ERROR_HTTP,
273             "header value seen at unexpected time");
274 0           return ctx->parse_status = PARSE_STATUS_ERROR;
275             }
276              
277 0           return 0;
278             }
279              
280 0           GIT_INLINE(bool) challenge_matches_scheme(
281             const char *challenge,
282             git_http_auth_scheme *scheme)
283             {
284 0           const char *scheme_name = scheme->name;
285 0           size_t scheme_len = strlen(scheme_name);
286              
287 0 0         if (!strncasecmp(challenge, scheme_name, scheme_len) &&
    0          
288 0 0         (challenge[scheme_len] == '\0' || challenge[scheme_len] == ' '))
289 0           return true;
290              
291 0           return false;
292             }
293              
294 0           static git_http_auth_scheme *scheme_for_challenge(const char *challenge)
295             {
296             size_t i;
297              
298 0 0         for (i = 0; i < ARRAY_SIZE(auth_schemes); i++) {
299 0 0         if (challenge_matches_scheme(challenge, &auth_schemes[i]))
300 0           return &auth_schemes[i];
301             }
302              
303 0           return NULL;
304             }
305              
306 0           GIT_INLINE(void) collect_authinfo(
307             unsigned int *schemetypes,
308             unsigned int *credtypes,
309             git_vector *challenges)
310             {
311             git_http_auth_scheme *scheme;
312             const char *challenge;
313             size_t i;
314              
315 0           *schemetypes = 0;
316 0           *credtypes = 0;
317              
318 0 0         git_vector_foreach(challenges, i, challenge) {
319 0 0         if ((scheme = scheme_for_challenge(challenge)) != NULL) {
320 0           *schemetypes |= scheme->type;
321 0           *credtypes |= scheme->credtypes;
322             }
323             }
324 0           }
325              
326 0           static int resend_needed(git_http_client *client, git_http_response *response)
327             {
328             git_http_auth_context *auth_context;
329              
330 0 0         if (response->status == GIT_HTTP_STATUS_UNAUTHORIZED &&
    0          
331 0 0         (auth_context = client->server.auth_context) &&
332 0 0         auth_context->is_complete &&
333 0           !auth_context->is_complete(auth_context))
334 0           return 1;
335              
336 0 0         if (response->status == GIT_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED &&
    0          
337 0 0         (auth_context = client->proxy.auth_context) &&
338 0 0         auth_context->is_complete &&
339 0           !auth_context->is_complete(auth_context))
340 0           return 1;
341              
342 0           return 0;
343             }
344              
345 0           static int on_headers_complete(http_parser *parser)
346             {
347 0           http_parser_context *ctx = (http_parser_context *) parser->data;
348              
349             /* Finalize the last seen header */
350 0           switch (ctx->parse_header_state) {
351             case PARSE_HEADER_VALUE:
352 0 0         if (on_header_complete(parser) < 0)
353 0           return ctx->parse_status = PARSE_STATUS_ERROR;
354              
355             /* Fall through */
356              
357             case PARSE_HEADER_NONE:
358 0           ctx->parse_header_state = PARSE_HEADER_COMPLETE;
359 0           break;
360              
361             default:
362 0           git_error_set(GIT_ERROR_HTTP,
363             "header completion at unexpected time");
364 0           return ctx->parse_status = PARSE_STATUS_ERROR;
365             }
366              
367 0           ctx->response->status = parser->status_code;
368 0           ctx->client->keepalive = http_should_keep_alive(parser);
369              
370             /* Prepare for authentication */
371 0           collect_authinfo(&ctx->response->server_auth_schemetypes,
372 0           &ctx->response->server_auth_credtypes,
373 0           &ctx->client->server.auth_challenges);
374 0           collect_authinfo(&ctx->response->proxy_auth_schemetypes,
375 0           &ctx->response->proxy_auth_credtypes,
376 0           &ctx->client->proxy.auth_challenges);
377              
378 0           ctx->response->resend_credentials = resend_needed(ctx->client,
379             ctx->response);
380              
381             /* Stop parsing. */
382 0           http_parser_pause(parser, 1);
383              
384 0 0         if (ctx->response->content_type || ctx->response->chunked)
    0          
385 0           ctx->client->state = READING_BODY;
386             else
387 0           ctx->client->state = DONE;
388              
389 0           return 0;
390             }
391              
392 0           static int on_body(http_parser *parser, const char *buf, size_t len)
393             {
394 0           http_parser_context *ctx = (http_parser_context *) parser->data;
395             size_t max_len;
396              
397             /* Saw data when we expected not to (eg, in consume_response_body) */
398 0 0         if (ctx->output_buf == NULL || ctx->output_size == 0) {
    0          
399 0           ctx->parse_status = PARSE_STATUS_NO_OUTPUT;
400 0           return 0;
401             }
402              
403 0 0         GIT_ASSERT(ctx->output_size >= ctx->output_written);
404              
405 0           max_len = min(ctx->output_size - ctx->output_written, len);
406 0           max_len = min(max_len, INT_MAX);
407              
408 0           memcpy(ctx->output_buf + ctx->output_written, buf, max_len);
409 0           ctx->output_written += max_len;
410              
411 0           return 0;
412             }
413              
414 0           static int on_message_complete(http_parser *parser)
415             {
416 0           http_parser_context *ctx = (http_parser_context *) parser->data;
417              
418 0           ctx->client->state = DONE;
419 0           return 0;
420             }
421              
422 0           GIT_INLINE(int) stream_write(
423             git_http_server *server,
424             const char *data,
425             size_t len)
426             {
427 0           git_trace(GIT_TRACE_TRACE,
428             "Sending request:\n%.*s", (int)len, data);
429              
430 0           return git_stream__write_full(server->stream, data, len, 0);
431             }
432              
433 0           GIT_INLINE(int) client_write_request(git_http_client *client)
434             {
435 0           git_stream *stream = client->current_server == PROXY ?
436 0 0         client->proxy.stream : client->server.stream;
437              
438 0           git_trace(GIT_TRACE_TRACE,
439             "Sending request:\n%.*s",
440 0           (int)client->request_msg.size, client->request_msg.ptr);
441              
442 0           return git_stream__write_full(stream,
443 0           client->request_msg.ptr,
444             client->request_msg.size,
445             0);
446             }
447              
448 0           static const char *name_for_method(git_http_method method)
449             {
450 0           switch (method) {
451             case GIT_HTTP_METHOD_GET:
452 0           return "GET";
453             case GIT_HTTP_METHOD_POST:
454 0           return "POST";
455             case GIT_HTTP_METHOD_CONNECT:
456 0           return "CONNECT";
457             }
458              
459 0           return NULL;
460             }
461              
462             /*
463             * Find the scheme that is suitable for the given credentials, based on the
464             * server's auth challenges.
465             */
466 0           static bool best_scheme_and_challenge(
467             git_http_auth_scheme **scheme_out,
468             const char **challenge_out,
469             git_vector *challenges,
470             git_credential *credentials)
471             {
472             const char *challenge;
473             size_t i, j;
474              
475 0 0         for (i = 0; i < ARRAY_SIZE(auth_schemes); i++) {
476 0 0         git_vector_foreach(challenges, j, challenge) {
477 0           git_http_auth_scheme *scheme = &auth_schemes[i];
478              
479 0 0         if (challenge_matches_scheme(challenge, scheme) &&
    0          
480 0           (scheme->credtypes & credentials->credtype)) {
481 0           *scheme_out = scheme;
482 0           *challenge_out = challenge;
483 0           return true;
484             }
485             }
486             }
487              
488 0           return false;
489             }
490              
491             /*
492             * Find the challenge from the server for our current auth context.
493             */
494 0           static const char *challenge_for_context(
495             git_vector *challenges,
496             git_http_auth_context *auth_ctx)
497             {
498             const char *challenge;
499             size_t i, j;
500              
501 0 0         for (i = 0; i < ARRAY_SIZE(auth_schemes); i++) {
502 0 0         if (auth_schemes[i].type == auth_ctx->type) {
503 0           git_http_auth_scheme *scheme = &auth_schemes[i];
504              
505 0 0         git_vector_foreach(challenges, j, challenge) {
506 0 0         if (challenge_matches_scheme(challenge, scheme))
507 0           return challenge;
508             }
509             }
510             }
511              
512 0           return NULL;
513             }
514              
515 0           static const char *init_auth_context(
516             git_http_server *server,
517             git_vector *challenges,
518             git_credential *credentials)
519             {
520             git_http_auth_scheme *scheme;
521             const char *challenge;
522             int error;
523              
524 0 0         if (!best_scheme_and_challenge(&scheme, &challenge, challenges, credentials)) {
525 0           git_error_set(GIT_ERROR_HTTP, "could not find appropriate mechanism for credentials");
526 0           return NULL;
527             }
528              
529 0           error = scheme->init_context(&server->auth_context, &server->url);
530              
531 0 0         if (error == GIT_PASSTHROUGH) {
532 0           git_error_set(GIT_ERROR_HTTP, "'%s' authentication is not supported", scheme->name);
533 0           return NULL;
534             }
535              
536 0           return challenge;
537             }
538              
539 0           static void free_auth_context(git_http_server *server)
540             {
541 0 0         if (!server->auth_context)
542 0           return;
543              
544 0 0         if (server->auth_context->free)
545 0           server->auth_context->free(server->auth_context);
546              
547 0           server->auth_context = NULL;
548             }
549              
550 0           static int apply_credentials(
551             git_str *buf,
552             git_http_server *server,
553             const char *header_name,
554             git_credential *credentials)
555             {
556 0           git_http_auth_context *auth = server->auth_context;
557 0           git_vector *challenges = &server->auth_challenges;
558             const char *challenge;
559 0           git_str token = GIT_STR_INIT;
560 0           int error = 0;
561              
562             /* We've started a new request without creds; free the context. */
563 0 0         if (auth && !credentials) {
    0          
564 0           free_auth_context(server);
565 0           return 0;
566             }
567              
568             /* We haven't authenticated, nor were we asked to. Nothing to do. */
569 0 0         if (!auth && !git_vector_length(challenges))
    0          
570 0           return 0;
571              
572 0 0         if (!auth) {
573 0           challenge = init_auth_context(server, challenges, credentials);
574 0           auth = server->auth_context;
575              
576 0 0         if (!challenge || !auth) {
    0          
577 0           error = -1;
578 0           goto done;
579             }
580 0 0         } else if (auth->set_challenge) {
581 0           challenge = challenge_for_context(challenges, auth);
582             }
583              
584 0 0         if (auth->set_challenge && challenge &&
    0          
    0          
585 0           (error = auth->set_challenge(auth, challenge)) < 0)
586 0           goto done;
587              
588 0 0         if ((error = auth->next_token(&token, auth, credentials)) < 0)
589 0           goto done;
590              
591 0 0         if (auth->is_complete && auth->is_complete(auth)) {
    0          
592             /*
593             * If we're done with an auth mechanism with connection affinity,
594             * we don't need to send any more headers and can dispose the context.
595             */
596 0 0         if (auth->connection_affinity)
597 0           free_auth_context(server);
598 0 0         } else if (!token.size) {
599 0           git_error_set(GIT_ERROR_HTTP, "failed to respond to authentication challenge");
600 0           error = GIT_EAUTH;
601 0           goto done;
602             }
603              
604 0 0         if (token.size > 0)
605 0           error = git_str_printf(buf, "%s: %s\r\n", header_name, token.ptr);
606              
607             done:
608 0           git_str_dispose(&token);
609 0           return error;
610             }
611              
612 0           GIT_INLINE(int) apply_server_credentials(
613             git_str *buf,
614             git_http_client *client,
615             git_http_request *request)
616             {
617 0           return apply_credentials(buf,
618             &client->server,
619             "Authorization",
620             request->credentials);
621             }
622              
623 0           GIT_INLINE(int) apply_proxy_credentials(
624             git_str *buf,
625             git_http_client *client,
626             git_http_request *request)
627             {
628 0           return apply_credentials(buf,
629             &client->proxy,
630             "Proxy-Authorization",
631             request->proxy_credentials);
632             }
633              
634 0           static int puts_host_and_port(git_str *buf, git_net_url *url, bool force_port)
635             {
636 0           bool ipv6 = git_net_url_is_ipv6(url);
637              
638 0 0         if (ipv6)
639 0           git_str_putc(buf, '[');
640              
641 0           git_str_puts(buf, url->host);
642              
643 0 0         if (ipv6)
644 0           git_str_putc(buf, ']');
645              
646 0 0         if (force_port || !git_net_url_is_default_port(url)) {
    0          
647 0           git_str_putc(buf, ':');
648 0           git_str_puts(buf, url->port);
649             }
650              
651 0 0         return git_str_oom(buf) ? -1 : 0;
652             }
653              
654 0           static int generate_connect_request(
655             git_http_client *client,
656             git_http_request *request)
657             {
658             git_str *buf;
659             int error;
660              
661 0           git_str_clear(&client->request_msg);
662 0           buf = &client->request_msg;
663              
664 0           git_str_puts(buf, "CONNECT ");
665 0           puts_host_and_port(buf, &client->server.url, true);
666 0           git_str_puts(buf, " HTTP/1.1\r\n");
667              
668 0           git_str_puts(buf, "User-Agent: ");
669 0           git_http__user_agent(buf);
670 0           git_str_puts(buf, "\r\n");
671              
672 0           git_str_puts(buf, "Host: ");
673 0           puts_host_and_port(buf, &client->server.url, true);
674 0           git_str_puts(buf, "\r\n");
675              
676 0 0         if ((error = apply_proxy_credentials(buf, client, request) < 0))
677 0           return -1;
678              
679 0           git_str_puts(buf, "\r\n");
680              
681 0 0         return git_str_oom(buf) ? -1 : 0;
682             }
683              
684 0           static bool use_connect_proxy(git_http_client *client)
685             {
686 0 0         return client->proxy.url.host && !strcmp(client->server.url.scheme, "https");
    0          
687             }
688              
689 0           static int generate_request(
690             git_http_client *client,
691             git_http_request *request)
692             {
693             git_str *buf;
694             size_t i;
695             int error;
696              
697 0 0         GIT_ASSERT_ARG(client);
698 0 0         GIT_ASSERT_ARG(request);
699              
700 0           git_str_clear(&client->request_msg);
701 0           buf = &client->request_msg;
702              
703             /* GET|POST path HTTP/1.1 */
704 0           git_str_puts(buf, name_for_method(request->method));
705 0           git_str_putc(buf, ' ');
706              
707 0 0         if (request->proxy && strcmp(request->url->scheme, "https"))
    0          
708 0           git_net_url_fmt(buf, request->url);
709             else
710 0           git_net_url_fmt_path(buf, request->url);
711              
712 0           git_str_puts(buf, " HTTP/1.1\r\n");
713              
714 0           git_str_puts(buf, "User-Agent: ");
715 0           git_http__user_agent(buf);
716 0           git_str_puts(buf, "\r\n");
717              
718 0           git_str_puts(buf, "Host: ");
719 0           puts_host_and_port(buf, request->url, false);
720 0           git_str_puts(buf, "\r\n");
721              
722 0 0         if (request->accept)
723 0           git_str_printf(buf, "Accept: %s\r\n", request->accept);
724             else
725 0           git_str_puts(buf, "Accept: */*\r\n");
726              
727 0 0         if (request->content_type)
728 0           git_str_printf(buf, "Content-Type: %s\r\n",
729             request->content_type);
730              
731 0 0         if (request->chunked)
732 0           git_str_puts(buf, "Transfer-Encoding: chunked\r\n");
733              
734 0 0         if (request->content_length > 0)
735 0           git_str_printf(buf, "Content-Length: %"PRIuZ "\r\n",
736             request->content_length);
737              
738 0 0         if (request->expect_continue)
739 0           git_str_printf(buf, "Expect: 100-continue\r\n");
740              
741 0           if ((error = apply_server_credentials(buf, client, request)) < 0 ||
742 0 0         (!use_connect_proxy(client) &&
743             (error = apply_proxy_credentials(buf, client, request)) < 0))
744 0           return error;
745              
746 0 0         if (request->custom_headers) {
747 0 0         for (i = 0; i < request->custom_headers->count; i++) {
748 0           const char *hdr = request->custom_headers->strings[i];
749              
750 0 0         if (hdr)
751 0           git_str_printf(buf, "%s\r\n", hdr);
752             }
753             }
754              
755 0           git_str_puts(buf, "\r\n");
756              
757 0 0         if (git_str_oom(buf))
758 0           return -1;
759              
760 0           return 0;
761             }
762              
763 0           static int check_certificate(
764             git_stream *stream,
765             git_net_url *url,
766             int is_valid,
767             git_transport_certificate_check_cb cert_cb,
768             void *cert_cb_payload)
769             {
770             git_cert *cert;
771 0           git_error_state last_error = {0};
772             int error;
773              
774 0 0         if ((error = git_stream_certificate(&cert, stream)) < 0)
775 0           return error;
776              
777 0           git_error_state_capture(&last_error, GIT_ECERTIFICATE);
778              
779 0           error = cert_cb(cert, is_valid, url->host, cert_cb_payload);
780              
781 0 0         if (error == GIT_PASSTHROUGH && !is_valid)
    0          
782 0           return git_error_state_restore(&last_error);
783 0 0         else if (error == GIT_PASSTHROUGH)
784 0           error = 0;
785 0 0         else if (error && !git_error_last())
    0          
786 0           git_error_set(GIT_ERROR_HTTP,
787             "user rejected certificate for %s", url->host);
788              
789 0           git_error_state_free(&last_error);
790 0           return error;
791             }
792              
793 0           static int server_connect_stream(
794             git_http_server *server,
795             git_transport_certificate_check_cb cert_cb,
796             void *cb_payload)
797             {
798             int error;
799              
800 0 0         GIT_ERROR_CHECK_VERSION(server->stream, GIT_STREAM_VERSION, "git_stream");
801              
802 0           error = git_stream_connect(server->stream);
803              
804 0 0         if (error && error != GIT_ECERTIFICATE)
    0          
805 0           return error;
806              
807 0 0         if (git_stream_is_encrypted(server->stream) && cert_cb != NULL)
    0          
808 0           error = check_certificate(server->stream, &server->url, !error,
809             cert_cb, cb_payload);
810              
811 0           return error;
812             }
813              
814 0           static void reset_auth_connection(git_http_server *server)
815             {
816             /*
817             * If we've authenticated and we're doing "normal"
818             * authentication with a request affinity (Basic, Digest)
819             * then we want to _keep_ our context, since authentication
820             * survives even through non-keep-alive connections. If
821             * we've authenticated and we're doing connection-based
822             * authentication (NTLM, Negotiate) - indicated by the presence
823             * of an `is_complete` callback - then we need to restart
824             * authentication on a new connection.
825             */
826              
827 0 0         if (server->auth_context &&
    0          
828 0           server->auth_context->connection_affinity)
829 0           free_auth_context(server);
830 0           }
831              
832             /*
833             * Updates the server data structure with the new URL; returns 1 if the server
834             * has changed and we need to reconnect, returns 0 otherwise.
835             */
836 0           GIT_INLINE(int) server_setup_from_url(
837             git_http_server *server,
838             git_net_url *url)
839             {
840 0 0         if (!server->url.scheme || strcmp(server->url.scheme, url->scheme) ||
    0          
    0          
841 0 0         !server->url.host || strcmp(server->url.host, url->host) ||
    0          
842 0 0         !server->url.port || strcmp(server->url.port, url->port)) {
843 0           git__free(server->url.scheme);
844 0           git__free(server->url.host);
845 0           git__free(server->url.port);
846              
847 0           server->url.scheme = git__strdup(url->scheme);
848 0 0         GIT_ERROR_CHECK_ALLOC(server->url.scheme);
849              
850 0           server->url.host = git__strdup(url->host);
851 0 0         GIT_ERROR_CHECK_ALLOC(server->url.host);
852              
853 0           server->url.port = git__strdup(url->port);
854 0 0         GIT_ERROR_CHECK_ALLOC(server->url.port);
855              
856 0           return 1;
857             }
858              
859 0           return 0;
860             }
861              
862 0           static void reset_parser(git_http_client *client)
863             {
864 0           http_parser_init(&client->parser, HTTP_RESPONSE);
865 0           }
866              
867 0           static int setup_hosts(
868             git_http_client *client,
869             git_http_request *request)
870             {
871 0           int ret, diff = 0;
872              
873 0 0         GIT_ASSERT_ARG(client);
874 0 0         GIT_ASSERT_ARG(request);
875              
876 0 0         GIT_ASSERT(request->url);
877              
878 0 0         if ((ret = server_setup_from_url(&client->server, request->url)) < 0)
879 0           return ret;
880              
881 0           diff |= ret;
882              
883 0 0         if (request->proxy &&
    0          
884 0           (ret = server_setup_from_url(&client->proxy, request->proxy)) < 0)
885 0           return ret;
886              
887 0           diff |= ret;
888              
889 0 0         if (diff) {
890 0           free_auth_context(&client->server);
891 0           free_auth_context(&client->proxy);
892              
893 0           client->connected = 0;
894             }
895              
896 0           return 0;
897             }
898              
899 0           GIT_INLINE(int) server_create_stream(git_http_server *server)
900             {
901 0           git_net_url *url = &server->url;
902              
903 0 0         if (strcasecmp(url->scheme, "https") == 0)
904 0           return git_tls_stream_new(&server->stream, url->host, url->port);
905 0 0         else if (strcasecmp(url->scheme, "http") == 0)
906 0           return git_socket_stream_new(&server->stream, url->host, url->port);
907              
908 0           git_error_set(GIT_ERROR_HTTP, "unknown http scheme '%s'", url->scheme);
909 0           return -1;
910             }
911              
912 0           GIT_INLINE(void) save_early_response(
913             git_http_client *client,
914             git_http_response *response)
915             {
916             /* Buffer the response so we can return it in read_response */
917 0           client->state = HAS_EARLY_RESPONSE;
918              
919 0           memcpy(&client->early_response, response, sizeof(git_http_response));
920 0           memset(response, 0, sizeof(git_http_response));
921 0           }
922              
923 0           static int proxy_connect(
924             git_http_client *client,
925             git_http_request *request)
926             {
927 0           git_http_response response = {0};
928             int error;
929              
930 0 0         if (!client->proxy_connected || !client->keepalive) {
    0          
931 0           git_trace(GIT_TRACE_DEBUG, "Connecting to proxy %s port %s",
932             client->proxy.url.host, client->proxy.url.port);
933              
934 0 0         if ((error = server_create_stream(&client->proxy)) < 0 ||
    0          
935 0           (error = server_connect_stream(&client->proxy,
936             client->opts.proxy_certificate_check_cb,
937             client->opts.proxy_certificate_check_payload)) < 0)
938             goto done;
939              
940 0           client->proxy_connected = 1;
941             }
942              
943 0           client->current_server = PROXY;
944 0           client->state = SENDING_REQUEST;
945              
946 0 0         if ((error = generate_connect_request(client, request)) < 0 ||
    0          
947             (error = client_write_request(client)) < 0)
948             goto done;
949              
950 0           client->state = SENT_REQUEST;
951              
952 0 0         if ((error = git_http_client_read_response(&response, client)) < 0 ||
    0          
953             (error = git_http_client_skip_body(client)) < 0)
954             goto done;
955              
956 0 0         GIT_ASSERT(client->state == DONE);
957              
958 0 0         if (response.status == GIT_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED) {
959 0           save_early_response(client, &response);
960              
961 0           error = GIT_RETRY;
962 0           goto done;
963 0 0         } else if (response.status != GIT_HTTP_STATUS_OK) {
964 0           git_error_set(GIT_ERROR_HTTP, "proxy returned unexpected status: %d", response.status);
965 0           error = -1;
966 0           goto done;
967             }
968              
969 0           reset_parser(client);
970 0           client->state = NONE;
971              
972             done:
973 0           git_http_response_dispose(&response);
974 0           return error;
975             }
976              
977 0           static int server_connect(git_http_client *client)
978             {
979 0           git_net_url *url = &client->server.url;
980             git_transport_certificate_check_cb cert_cb;
981             void *cert_payload;
982             int error;
983              
984 0           client->current_server = SERVER;
985              
986 0 0         if (client->proxy.stream)
987 0           error = git_tls_stream_wrap(&client->server.stream, client->proxy.stream, url->host);
988             else
989 0           error = server_create_stream(&client->server);
990              
991 0 0         if (error < 0)
992 0           goto done;
993              
994 0           cert_cb = client->opts.server_certificate_check_cb;
995 0           cert_payload = client->opts.server_certificate_check_payload;
996              
997 0           error = server_connect_stream(&client->server, cert_cb, cert_payload);
998              
999             done:
1000 0           return error;
1001             }
1002              
1003 0           GIT_INLINE(void) close_stream(git_http_server *server)
1004             {
1005 0 0         if (server->stream) {
1006 0           git_stream_close(server->stream);
1007 0           git_stream_free(server->stream);
1008 0           server->stream = NULL;
1009             }
1010 0           }
1011              
1012 0           static int http_client_connect(
1013             git_http_client *client,
1014             git_http_request *request)
1015             {
1016 0           bool use_proxy = false;
1017             int error;
1018              
1019 0 0         if ((error = setup_hosts(client, request)) < 0)
1020 0           goto on_error;
1021              
1022             /* We're connected to our destination server; no need to reconnect */
1023 0 0         if (client->connected && client->keepalive &&
    0          
    0          
1024 0 0         (client->state == NONE || client->state == DONE))
1025 0           return 0;
1026              
1027 0           client->connected = 0;
1028 0           client->request_count = 0;
1029              
1030 0           close_stream(&client->server);
1031 0           reset_auth_connection(&client->server);
1032              
1033 0           reset_parser(client);
1034              
1035             /* Reconnect to the proxy if necessary. */
1036 0           use_proxy = use_connect_proxy(client);
1037              
1038 0 0         if (use_proxy) {
1039 0 0         if (!client->proxy_connected || !client->keepalive ||
    0          
    0          
1040 0 0         (client->state != NONE && client->state != DONE)) {
1041 0           close_stream(&client->proxy);
1042 0           reset_auth_connection(&client->proxy);
1043              
1044 0           client->proxy_connected = 0;
1045             }
1046              
1047 0 0         if ((error = proxy_connect(client, request)) < 0)
1048 0           goto on_error;
1049             }
1050              
1051 0           git_trace(GIT_TRACE_DEBUG, "Connecting to remote %s port %s",
1052             client->server.url.host, client->server.url.port);
1053              
1054 0 0         if ((error = server_connect(client)) < 0)
1055 0           goto on_error;
1056              
1057 0           client->connected = 1;
1058 0           return error;
1059              
1060             on_error:
1061 0 0         if (error != GIT_RETRY)
1062 0           close_stream(&client->proxy);
1063              
1064 0           close_stream(&client->server);
1065 0           return error;
1066             }
1067              
1068 0           GIT_INLINE(int) client_read(git_http_client *client)
1069             {
1070 0           http_parser_context *parser_context = client->parser.data;
1071             git_stream *stream;
1072 0           char *buf = client->read_buf.ptr + client->read_buf.size;
1073             size_t max_len;
1074             ssize_t read_len;
1075              
1076 0           stream = client->current_server == PROXY ?
1077 0 0         client->proxy.stream : client->server.stream;
1078              
1079             /*
1080             * We use a git_str for convenience, but statically allocate it and
1081             * don't resize. Limit our consumption to INT_MAX since calling
1082             * functions use an int return type to return number of bytes read.
1083             */
1084 0           max_len = client->read_buf.asize - client->read_buf.size;
1085 0           max_len = min(max_len, INT_MAX);
1086              
1087 0 0         if (parser_context->output_size)
1088 0           max_len = min(max_len, parser_context->output_size);
1089              
1090 0 0         if (max_len == 0) {
1091 0           git_error_set(GIT_ERROR_HTTP, "no room in output buffer");
1092 0           return -1;
1093             }
1094              
1095 0           read_len = git_stream_read(stream, buf, max_len);
1096              
1097 0 0         if (read_len >= 0) {
1098 0           client->read_buf.size += read_len;
1099              
1100 0           git_trace(GIT_TRACE_TRACE, "Received:\n%.*s",
1101             (int)read_len, buf);
1102             }
1103              
1104 0           return (int)read_len;
1105             }
1106              
1107             static bool parser_settings_initialized;
1108             static http_parser_settings parser_settings;
1109              
1110 0           GIT_INLINE(http_parser_settings *) http_client_parser_settings(void)
1111             {
1112 0 0         if (!parser_settings_initialized) {
1113 0           parser_settings.on_header_field = on_header_field;
1114 0           parser_settings.on_header_value = on_header_value;
1115 0           parser_settings.on_headers_complete = on_headers_complete;
1116 0           parser_settings.on_body = on_body;
1117 0           parser_settings.on_message_complete = on_message_complete;
1118              
1119 0           parser_settings_initialized = true;
1120             }
1121              
1122 0           return &parser_settings;
1123             }
1124              
1125 0           GIT_INLINE(int) client_read_and_parse(git_http_client *client)
1126             {
1127 0           http_parser *parser = &client->parser;
1128 0           http_parser_context *ctx = (http_parser_context *) parser->data;
1129             unsigned char http_errno;
1130             int read_len;
1131             size_t parsed_len;
1132              
1133             /*
1134             * If we have data in our read buffer, that means we stopped early
1135             * when parsing headers. Use the data in the read buffer instead of
1136             * reading more from the socket.
1137             */
1138 0 0         if (!client->read_buf.size && (read_len = client_read(client)) < 0)
    0          
1139 0           return read_len;
1140              
1141 0           parsed_len = http_parser_execute(parser,
1142 0           http_client_parser_settings(),
1143 0           client->read_buf.ptr,
1144             client->read_buf.size);
1145 0           http_errno = client->parser.http_errno;
1146              
1147 0 0         if (parsed_len > INT_MAX) {
1148 0           git_error_set(GIT_ERROR_HTTP, "unexpectedly large parse");
1149 0           return -1;
1150             }
1151              
1152 0 0         if (ctx->parse_status == PARSE_STATUS_ERROR) {
1153 0           client->connected = 0;
1154 0 0         return ctx->error ? ctx->error : -1;
1155             }
1156              
1157             /*
1158             * If we finished reading the headers or body, we paused parsing.
1159             * Otherwise the parser will start filling the body, or even parse
1160             * a new response if the server pipelined us multiple responses.
1161             * (This can happen in response to an expect/continue request,
1162             * where the server gives you a 100 and 200 simultaneously.)
1163             */
1164 0 0         if (http_errno == HPE_PAUSED) {
1165             /*
1166             * http-parser has a "feature" where it will not deliver the
1167             * final byte when paused in a callback. Consume that byte.
1168             * https://github.com/nodejs/http-parser/issues/97
1169             */
1170 0 0         GIT_ASSERT(client->read_buf.size > parsed_len);
1171              
1172 0           http_parser_pause(parser, 0);
1173              
1174 0           parsed_len += http_parser_execute(parser,
1175 0           http_client_parser_settings(),
1176 0           client->read_buf.ptr + parsed_len,
1177             1);
1178             }
1179              
1180             /* Most failures will be reported in http_errno */
1181 0 0         else if (parser->http_errno != HPE_OK) {
1182 0           git_error_set(GIT_ERROR_HTTP, "http parser error: %s",
1183             http_errno_description(http_errno));
1184 0           return -1;
1185             }
1186              
1187             /* Otherwise we should have consumed the entire buffer. */
1188 0 0         else if (parsed_len != client->read_buf.size) {
1189 0           git_error_set(GIT_ERROR_HTTP,
1190             "http parser did not consume entire buffer: %s",
1191             http_errno_description(http_errno));
1192 0           return -1;
1193             }
1194              
1195             /* recv returned 0, the server hung up on us */
1196 0 0         else if (!parsed_len) {
1197 0           git_error_set(GIT_ERROR_HTTP, "unexpected EOF");
1198 0           return -1;
1199             }
1200              
1201 0           git_str_consume_bytes(&client->read_buf, parsed_len);
1202              
1203 0           return (int)parsed_len;
1204             }
1205              
1206             /*
1207             * See if we've consumed the entire response body. If the client was
1208             * reading the body but did not consume it entirely, it's possible that
1209             * they knew that the stream had finished (in a git response, seeing a
1210             * final flush) and stopped reading. But if the response was chunked,
1211             * we may have not consumed the final chunk marker. Consume it to
1212             * ensure that we don't have it waiting in our socket. If there's
1213             * more than just a chunk marker, close the connection.
1214             */
1215 0           static void complete_response_body(git_http_client *client)
1216             {
1217 0           http_parser_context parser_context = {0};
1218              
1219             /* If we're not keeping alive, don't bother. */
1220 0 0         if (!client->keepalive) {
1221 0           client->connected = 0;
1222 0           goto done;
1223             }
1224              
1225 0           parser_context.client = client;
1226 0           client->parser.data = &parser_context;
1227              
1228             /* If there was an error, just close the connection. */
1229 0 0         if (client_read_and_parse(client) < 0 ||
    0          
1230 0 0         parser_context.error != HPE_OK ||
1231 0 0         (parser_context.parse_status != PARSE_STATUS_OK &&
1232 0           parser_context.parse_status != PARSE_STATUS_NO_OUTPUT)) {
1233 0           git_error_clear();
1234 0           client->connected = 0;
1235             }
1236              
1237             done:
1238 0           git_str_clear(&client->read_buf);
1239 0           }
1240              
1241 0           int git_http_client_send_request(
1242             git_http_client *client,
1243             git_http_request *request)
1244             {
1245 0           git_http_response response = {0};
1246 0           int error = -1;
1247              
1248 0 0         GIT_ASSERT_ARG(client);
1249 0 0         GIT_ASSERT_ARG(request);
1250              
1251             /* If the client did not finish reading, clean up the stream. */
1252 0 0         if (client->state == READING_BODY)
1253 0           complete_response_body(client);
1254              
1255             /* If we're waiting for proxy auth, don't sending more requests. */
1256 0 0         if (client->state == HAS_EARLY_RESPONSE)
1257 0           return 0;
1258              
1259 0 0         if (git_trace_level() >= GIT_TRACE_DEBUG) {
1260 0           git_str url = GIT_STR_INIT;
1261 0           git_net_url_fmt(&url, request->url);
1262 0 0         git_trace(GIT_TRACE_DEBUG, "Sending %s request to %s",
1263             name_for_method(request->method),
1264 0           url.ptr ? url.ptr : "");
1265 0           git_str_dispose(&url);
1266             }
1267              
1268 0 0         if ((error = http_client_connect(client, request)) < 0 ||
    0          
1269 0 0         (error = generate_request(client, request)) < 0 ||
1270             (error = client_write_request(client)) < 0)
1271             goto done;
1272              
1273 0           client->state = SENT_REQUEST;
1274              
1275 0 0         if (request->expect_continue) {
1276 0 0         if ((error = git_http_client_read_response(&response, client)) < 0 ||
    0          
1277             (error = git_http_client_skip_body(client)) < 0)
1278             goto done;
1279              
1280 0           error = 0;
1281              
1282 0 0         if (response.status != GIT_HTTP_STATUS_CONTINUE) {
1283 0           save_early_response(client, &response);
1284 0           goto done;
1285             }
1286             }
1287              
1288 0 0         if (request->content_length || request->chunked) {
    0          
1289 0           client->state = SENDING_BODY;
1290 0           client->request_body_len = request->content_length;
1291 0           client->request_body_remain = request->content_length;
1292 0           client->request_chunked = request->chunked;
1293             }
1294              
1295 0           reset_parser(client);
1296              
1297             done:
1298 0 0         if (error == GIT_RETRY)
1299 0           error = 0;
1300              
1301 0           git_http_response_dispose(&response);
1302 0           return error;
1303             }
1304              
1305 0           bool git_http_client_has_response(git_http_client *client)
1306             {
1307 0 0         return (client->state == HAS_EARLY_RESPONSE ||
    0          
1308 0           client->state > SENT_REQUEST);
1309             }
1310              
1311 0           int git_http_client_send_body(
1312             git_http_client *client,
1313             const char *buffer,
1314             size_t buffer_len)
1315             {
1316             git_http_server *server;
1317 0           git_str hdr = GIT_STR_INIT;
1318             int error;
1319              
1320 0 0         GIT_ASSERT_ARG(client);
1321              
1322             /* If we're waiting for proxy auth, don't sending more requests. */
1323 0 0         if (client->state == HAS_EARLY_RESPONSE)
1324 0           return 0;
1325              
1326 0 0         if (client->state != SENDING_BODY) {
1327 0           git_error_set(GIT_ERROR_HTTP, "client is in invalid state");
1328 0           return -1;
1329             }
1330              
1331 0 0         if (!buffer_len)
1332 0           return 0;
1333              
1334 0           server = &client->server;
1335              
1336 0 0         if (client->request_body_len) {
1337 0 0         GIT_ASSERT(buffer_len <= client->request_body_remain);
1338              
1339 0 0         if ((error = stream_write(server, buffer, buffer_len)) < 0)
1340 0           goto done;
1341              
1342 0           client->request_body_remain -= buffer_len;
1343             } else {
1344 0 0         if ((error = git_str_printf(&hdr, "%" PRIxZ "\r\n", buffer_len)) < 0 ||
    0          
1345 0 0         (error = stream_write(server, hdr.ptr, hdr.size)) < 0 ||
1346 0           (error = stream_write(server, buffer, buffer_len)) < 0 ||
1347             (error = stream_write(server, "\r\n", 2)) < 0)
1348             goto done;
1349             }
1350              
1351             done:
1352 0           git_str_dispose(&hdr);
1353 0           return error;
1354             }
1355              
1356 0           static int complete_request(git_http_client *client)
1357             {
1358 0           int error = 0;
1359              
1360 0 0         GIT_ASSERT_ARG(client);
1361 0 0         GIT_ASSERT(client->state == SENDING_BODY);
1362              
1363 0 0         if (client->request_body_len && client->request_body_remain) {
    0          
1364 0           git_error_set(GIT_ERROR_HTTP, "truncated write");
1365 0           error = -1;
1366 0 0         } else if (client->request_chunked) {
1367 0           error = stream_write(&client->server, "0\r\n\r\n", 5);
1368             }
1369              
1370 0           client->state = SENT_REQUEST;
1371 0           return error;
1372             }
1373              
1374 0           int git_http_client_read_response(
1375             git_http_response *response,
1376             git_http_client *client)
1377             {
1378 0           http_parser_context parser_context = {0};
1379             int error;
1380              
1381 0 0         GIT_ASSERT_ARG(response);
1382 0 0         GIT_ASSERT_ARG(client);
1383              
1384 0 0         if (client->state == SENDING_BODY) {
1385 0 0         if ((error = complete_request(client)) < 0)
1386 0           goto done;
1387             }
1388              
1389 0 0         if (client->state == HAS_EARLY_RESPONSE) {
1390 0           memcpy(response, &client->early_response, sizeof(git_http_response));
1391 0           memset(&client->early_response, 0, sizeof(git_http_response));
1392 0           client->state = DONE;
1393 0           return 0;
1394             }
1395              
1396 0 0         if (client->state != SENT_REQUEST) {
1397 0           git_error_set(GIT_ERROR_HTTP, "client is in invalid state");
1398 0           error = -1;
1399 0           goto done;
1400             }
1401              
1402 0           git_http_response_dispose(response);
1403              
1404 0 0         if (client->current_server == PROXY) {
1405 0           git_vector_free_deep(&client->proxy.auth_challenges);
1406 0 0         } else if(client->current_server == SERVER) {
1407 0           git_vector_free_deep(&client->server.auth_challenges);
1408             }
1409              
1410 0           client->state = READING_RESPONSE;
1411 0           client->keepalive = 0;
1412 0           client->parser.data = &parser_context;
1413              
1414 0           parser_context.client = client;
1415 0           parser_context.response = response;
1416              
1417 0 0         while (client->state == READING_RESPONSE) {
1418 0 0         if ((error = client_read_and_parse(client)) < 0)
1419 0           goto done;
1420             }
1421              
1422 0 0         GIT_ASSERT(client->state == READING_BODY || client->state == DONE);
    0          
1423              
1424             done:
1425 0           git_str_dispose(&parser_context.parse_header_name);
1426 0           git_str_dispose(&parser_context.parse_header_value);
1427              
1428 0           return error;
1429             }
1430              
1431 0           int git_http_client_read_body(
1432             git_http_client *client,
1433             char *buffer,
1434             size_t buffer_size)
1435             {
1436 0           http_parser_context parser_context = {0};
1437 0           int error = 0;
1438              
1439 0 0         if (client->state == DONE)
1440 0           return 0;
1441              
1442 0 0         if (client->state != READING_BODY) {
1443 0           git_error_set(GIT_ERROR_HTTP, "client is in invalid state");
1444 0           return -1;
1445             }
1446              
1447             /*
1448             * Now we'll read from the socket and http_parser will pipeline the
1449             * data directly to the client.
1450             */
1451              
1452 0           parser_context.client = client;
1453 0           parser_context.output_buf = buffer;
1454 0           parser_context.output_size = buffer_size;
1455              
1456 0           client->parser.data = &parser_context;
1457              
1458             /*
1459             * Clients expect to get a non-zero amount of data from us,
1460             * so we either block until we have data to return, until we
1461             * hit EOF or there's an error. Do this in a loop, since we
1462             * may end up reading only some stream metadata (like chunk
1463             * information).
1464             */
1465 0 0         while (!parser_context.output_written) {
1466 0           error = client_read_and_parse(client);
1467              
1468 0 0         if (error <= 0)
1469 0           goto done;
1470              
1471 0 0         if (client->state == DONE)
1472 0           break;
1473             }
1474              
1475 0 0         GIT_ASSERT(parser_context.output_written <= INT_MAX);
1476 0           error = (int)parser_context.output_written;
1477              
1478             done:
1479 0 0         if (error < 0)
1480 0           client->connected = 0;
1481              
1482 0           return error;
1483             }
1484              
1485 0           int git_http_client_skip_body(git_http_client *client)
1486             {
1487 0           http_parser_context parser_context = {0};
1488             int error;
1489              
1490 0 0         if (client->state == DONE)
1491 0           return 0;
1492              
1493 0 0         if (client->state != READING_BODY) {
1494 0           git_error_set(GIT_ERROR_HTTP, "client is in invalid state");
1495 0           return -1;
1496             }
1497              
1498 0           parser_context.client = client;
1499 0           client->parser.data = &parser_context;
1500              
1501             do {
1502 0           error = client_read_and_parse(client);
1503              
1504 0 0         if (parser_context.error != HPE_OK ||
    0          
1505 0 0         (parser_context.parse_status != PARSE_STATUS_OK &&
1506 0           parser_context.parse_status != PARSE_STATUS_NO_OUTPUT)) {
1507 0           git_error_set(GIT_ERROR_HTTP,
1508             "unexpected data handled in callback");
1509 0           error = -1;
1510             }
1511 0 0         } while (error >= 0 && client->state != DONE);
    0          
1512              
1513 0 0         if (error < 0)
1514 0           client->connected = 0;
1515              
1516 0           return error;
1517             }
1518              
1519             /*
1520             * Create an http_client capable of communicating with the given remote
1521             * host.
1522             */
1523 0           int git_http_client_new(
1524             git_http_client **out,
1525             git_http_client_options *opts)
1526             {
1527             git_http_client *client;
1528              
1529 0 0         GIT_ASSERT_ARG(out);
1530              
1531 0           client = git__calloc(1, sizeof(git_http_client));
1532 0 0         GIT_ERROR_CHECK_ALLOC(client);
1533              
1534 0           git_str_init(&client->read_buf, GIT_READ_BUFFER_SIZE);
1535 0 0         GIT_ERROR_CHECK_ALLOC(client->read_buf.ptr);
1536              
1537 0 0         if (opts)
1538 0           memcpy(&client->opts, opts, sizeof(git_http_client_options));
1539              
1540 0           *out = client;
1541 0           return 0;
1542             }
1543              
1544 0           GIT_INLINE(void) http_server_close(git_http_server *server)
1545             {
1546 0 0         if (server->stream) {
1547 0           git_stream_close(server->stream);
1548 0           git_stream_free(server->stream);
1549 0           server->stream = NULL;
1550             }
1551              
1552 0           git_net_url_dispose(&server->url);
1553              
1554 0           git_vector_free_deep(&server->auth_challenges);
1555 0           free_auth_context(server);
1556 0           }
1557              
1558 0           static void http_client_close(git_http_client *client)
1559             {
1560 0           http_server_close(&client->server);
1561 0           http_server_close(&client->proxy);
1562              
1563 0           git_str_dispose(&client->request_msg);
1564              
1565 0           client->state = 0;
1566 0           client->request_count = 0;
1567 0           client->connected = 0;
1568 0           client->keepalive = 0;
1569 0           }
1570              
1571 0           void git_http_client_free(git_http_client *client)
1572             {
1573 0 0         if (!client)
1574 0           return;
1575              
1576 0           http_client_close(client);
1577 0           git_str_dispose(&client->read_buf);
1578 0           git__free(client);
1579             }