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