]> git.proxmox.com Git - libgit2.git/blob - src/transports/http.c
Merge pull request #3377 from dleehr/fix-push-cb
[libgit2.git] / src / transports / http.c
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 #ifndef GIT_WINHTTP
8
9 #include "git2.h"
10 #include "http_parser.h"
11 #include "buffer.h"
12 #include "netops.h"
13 #include "remote.h"
14 #include "smart.h"
15 #include "auth.h"
16 #include "auth_negotiate.h"
17 #include "tls_stream.h"
18 #include "socket_stream.h"
19 #include "curl_stream.h"
20
21 git_http_auth_scheme auth_schemes[] = {
22 { GIT_AUTHTYPE_NEGOTIATE, "Negotiate", GIT_CREDTYPE_DEFAULT, git_http_auth_negotiate },
23 { GIT_AUTHTYPE_BASIC, "Basic", GIT_CREDTYPE_USERPASS_PLAINTEXT, git_http_auth_basic },
24 };
25
26 static const char *upload_pack_service = "upload-pack";
27 static const char *upload_pack_ls_service_url = "/info/refs?service=git-upload-pack";
28 static const char *upload_pack_service_url = "/git-upload-pack";
29 static const char *receive_pack_service = "receive-pack";
30 static const char *receive_pack_ls_service_url = "/info/refs?service=git-receive-pack";
31 static const char *receive_pack_service_url = "/git-receive-pack";
32 static const char *get_verb = "GET";
33 static const char *post_verb = "POST";
34
35 #define OWNING_SUBTRANSPORT(s) ((http_subtransport *)(s)->parent.subtransport)
36
37 #define PARSE_ERROR_GENERIC -1
38 #define PARSE_ERROR_REPLAY -2
39 /** Look at the user field */
40 #define PARSE_ERROR_EXT -3
41
42 #define CHUNK_SIZE 4096
43
44 enum last_cb {
45 NONE,
46 FIELD,
47 VALUE
48 };
49
50 typedef struct {
51 git_smart_subtransport_stream parent;
52 const char *service;
53 const char *service_url;
54 char *redirect_url;
55 const char *verb;
56 char *chunk_buffer;
57 unsigned chunk_buffer_len;
58 unsigned sent_request : 1,
59 received_response : 1,
60 chunked : 1,
61 redirect_count : 3;
62 } http_stream;
63
64 typedef struct {
65 git_smart_subtransport parent;
66 transport_smart *owner;
67 git_stream *io;
68 gitno_connection_data connection_data;
69 bool connected;
70
71 /* Parser structures */
72 http_parser parser;
73 http_parser_settings settings;
74 gitno_buffer parse_buffer;
75 git_buf parse_header_name;
76 git_buf parse_header_value;
77 char parse_buffer_data[NETIO_BUFSIZE];
78 char *content_type;
79 char *location;
80 git_vector www_authenticate;
81 enum last_cb last_cb;
82 int parse_error;
83 int error;
84 unsigned parse_finished : 1;
85
86 /* Authentication */
87 git_cred *cred;
88 git_cred *url_cred;
89 git_vector auth_contexts;
90 } http_subtransport;
91
92 typedef struct {
93 http_stream *s;
94 http_subtransport *t;
95
96 /* Target buffer details from read() */
97 char *buffer;
98 size_t buf_size;
99 size_t *bytes_read;
100 } parser_context;
101
102 static bool credtype_match(git_http_auth_scheme *scheme, void *data)
103 {
104 unsigned int credtype = *(unsigned int *)data;
105
106 return !!(scheme->credtypes & credtype);
107 }
108
109 static bool challenge_match(git_http_auth_scheme *scheme, void *data)
110 {
111 const char *scheme_name = scheme->name;
112 const char *challenge = (const char *)data;
113 size_t scheme_len;
114
115 scheme_len = strlen(scheme_name);
116 return (strncmp(challenge, scheme_name, scheme_len) == 0 &&
117 (challenge[scheme_len] == '\0' || challenge[scheme_len] == ' '));
118 }
119
120 static int auth_context_match(
121 git_http_auth_context **out,
122 http_subtransport *t,
123 bool (*scheme_match)(git_http_auth_scheme *scheme, void *data),
124 void *data)
125 {
126 git_http_auth_scheme *scheme = NULL;
127 git_http_auth_context *context = NULL, *c;
128 size_t i;
129
130 *out = NULL;
131
132 for (i = 0; i < ARRAY_SIZE(auth_schemes); i++) {
133 if (scheme_match(&auth_schemes[i], data)) {
134 scheme = &auth_schemes[i];
135 break;
136 }
137 }
138
139 if (!scheme)
140 return 0;
141
142 /* See if authentication has already started for this scheme */
143 git_vector_foreach(&t->auth_contexts, i, c) {
144 if (c->type == scheme->type) {
145 context = c;
146 break;
147 }
148 }
149
150 if (!context) {
151 if (scheme->init_context(&context, &t->connection_data) < 0)
152 return -1;
153 else if (!context)
154 return 0;
155 else if (git_vector_insert(&t->auth_contexts, context) < 0)
156 return -1;
157 }
158
159 *out = context;
160
161 return 0;
162 }
163
164 static int apply_credentials(git_buf *buf, http_subtransport *t)
165 {
166 git_cred *cred = t->cred;
167 git_http_auth_context *context;
168
169 /* Apply the credentials given to us in the URL */
170 if (!cred && t->connection_data.user && t->connection_data.pass) {
171 if (!t->url_cred &&
172 git_cred_userpass_plaintext_new(&t->url_cred,
173 t->connection_data.user, t->connection_data.pass) < 0)
174 return -1;
175
176 cred = t->url_cred;
177 }
178
179 if (!cred)
180 return 0;
181
182 /* Get or create a context for the best scheme for this cred type */
183 if (auth_context_match(&context, t, credtype_match, &cred->credtype) < 0)
184 return -1;
185
186 return context->next_token(buf, context, cred);
187 }
188
189 static int gen_request(
190 git_buf *buf,
191 http_stream *s,
192 size_t content_length)
193 {
194 http_subtransport *t = OWNING_SUBTRANSPORT(s);
195 const char *path = t->connection_data.path ? t->connection_data.path : "/";
196
197 git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, path, s->service_url);
198
199 git_buf_puts(buf, "User-Agent: git/1.0 (libgit2 " LIBGIT2_VERSION ")\r\n");
200 git_buf_printf(buf, "Host: %s\r\n", t->connection_data.host);
201
202 if (s->chunked || content_length > 0) {
203 git_buf_printf(buf, "Accept: application/x-git-%s-result\r\n", s->service);
204 git_buf_printf(buf, "Content-Type: application/x-git-%s-request\r\n", s->service);
205
206 if (s->chunked)
207 git_buf_puts(buf, "Transfer-Encoding: chunked\r\n");
208 else
209 git_buf_printf(buf, "Content-Length: %"PRIuZ "\r\n", content_length);
210 } else
211 git_buf_puts(buf, "Accept: */*\r\n");
212
213 /* Apply credentials to the request */
214 if (apply_credentials(buf, t) < 0)
215 return -1;
216
217 git_buf_puts(buf, "\r\n");
218
219 if (git_buf_oom(buf))
220 return -1;
221
222 return 0;
223 }
224
225 static int parse_authenticate_response(
226 git_vector *www_authenticate,
227 http_subtransport *t,
228 int *allowed_types)
229 {
230 git_http_auth_context *context;
231 char *challenge;
232 size_t i;
233
234 git_vector_foreach(www_authenticate, i, challenge) {
235 if (auth_context_match(&context, t, challenge_match, challenge) < 0)
236 return -1;
237 else if (!context)
238 continue;
239
240 if (context->set_challenge &&
241 context->set_challenge(context, challenge) < 0)
242 return -1;
243
244 *allowed_types |= context->credtypes;
245 }
246
247 return 0;
248 }
249
250 static int on_header_ready(http_subtransport *t)
251 {
252 git_buf *name = &t->parse_header_name;
253 git_buf *value = &t->parse_header_value;
254
255 if (!strcasecmp("Content-Type", git_buf_cstr(name))) {
256 if (!t->content_type) {
257 t->content_type = git__strdup(git_buf_cstr(value));
258 GITERR_CHECK_ALLOC(t->content_type);
259 }
260 }
261 else if (!strcasecmp("WWW-Authenticate", git_buf_cstr(name))) {
262 char *dup = git__strdup(git_buf_cstr(value));
263 GITERR_CHECK_ALLOC(dup);
264
265 git_vector_insert(&t->www_authenticate, dup);
266 }
267 else if (!strcasecmp("Location", git_buf_cstr(name))) {
268 if (!t->location) {
269 t->location = git__strdup(git_buf_cstr(value));
270 GITERR_CHECK_ALLOC(t->location);
271 }
272 }
273
274 return 0;
275 }
276
277 static int on_header_field(http_parser *parser, const char *str, size_t len)
278 {
279 parser_context *ctx = (parser_context *) parser->data;
280 http_subtransport *t = ctx->t;
281
282 /* Both parse_header_name and parse_header_value are populated
283 * and ready for consumption */
284 if (VALUE == t->last_cb)
285 if (on_header_ready(t) < 0)
286 return t->parse_error = PARSE_ERROR_GENERIC;
287
288 if (NONE == t->last_cb || VALUE == t->last_cb)
289 git_buf_clear(&t->parse_header_name);
290
291 if (git_buf_put(&t->parse_header_name, str, len) < 0)
292 return t->parse_error = PARSE_ERROR_GENERIC;
293
294 t->last_cb = FIELD;
295 return 0;
296 }
297
298 static int on_header_value(http_parser *parser, const char *str, size_t len)
299 {
300 parser_context *ctx = (parser_context *) parser->data;
301 http_subtransport *t = ctx->t;
302
303 assert(NONE != t->last_cb);
304
305 if (FIELD == t->last_cb)
306 git_buf_clear(&t->parse_header_value);
307
308 if (git_buf_put(&t->parse_header_value, str, len) < 0)
309 return t->parse_error = PARSE_ERROR_GENERIC;
310
311 t->last_cb = VALUE;
312 return 0;
313 }
314
315 static int on_headers_complete(http_parser *parser)
316 {
317 parser_context *ctx = (parser_context *) parser->data;
318 http_subtransport *t = ctx->t;
319 http_stream *s = ctx->s;
320 git_buf buf = GIT_BUF_INIT;
321 int error = 0, no_callback = 0, allowed_auth_types = 0;
322
323 /* Both parse_header_name and parse_header_value are populated
324 * and ready for consumption. */
325 if (VALUE == t->last_cb)
326 if (on_header_ready(t) < 0)
327 return t->parse_error = PARSE_ERROR_GENERIC;
328
329 /* Capture authentication headers which may be a 401 (authentication
330 * is not complete) or a 200 (simply informing us that auth *is*
331 * complete.)
332 */
333 if (parse_authenticate_response(&t->www_authenticate, t,
334 &allowed_auth_types) < 0)
335 return t->parse_error = PARSE_ERROR_GENERIC;
336
337 /* Check for an authentication failure. */
338 if (parser->status_code == 401 && get_verb == s->verb) {
339 if (!t->owner->cred_acquire_cb) {
340 no_callback = 1;
341 } else {
342 if (allowed_auth_types) {
343 if (t->cred) {
344 t->cred->free(t->cred);
345 t->cred = NULL;
346 }
347
348 error = t->owner->cred_acquire_cb(&t->cred,
349 t->owner->url,
350 t->connection_data.user,
351 allowed_auth_types,
352 t->owner->cred_acquire_payload);
353
354 if (error == GIT_PASSTHROUGH) {
355 no_callback = 1;
356 } else if (error < 0) {
357 t->error = error;
358 return t->parse_error = PARSE_ERROR_EXT;
359 } else {
360 assert(t->cred);
361
362 if (!(t->cred->credtype & allowed_auth_types)) {
363 giterr_set(GITERR_NET, "credentials callback returned an invalid cred type");
364 return t->parse_error = PARSE_ERROR_GENERIC;
365 }
366
367 /* Successfully acquired a credential. */
368 t->parse_error = PARSE_ERROR_REPLAY;
369 return 0;
370 }
371 }
372 }
373
374 if (no_callback) {
375 giterr_set(GITERR_NET, "authentication required but no callback set");
376 return t->parse_error = PARSE_ERROR_GENERIC;
377 }
378 }
379
380 /* Check for a redirect.
381 * Right now we only permit a redirect to the same hostname. */
382 if ((parser->status_code == 301 ||
383 parser->status_code == 302 ||
384 (parser->status_code == 303 && get_verb == s->verb) ||
385 parser->status_code == 307) &&
386 t->location) {
387
388 if (s->redirect_count >= 7) {
389 giterr_set(GITERR_NET, "Too many redirects");
390 return t->parse_error = PARSE_ERROR_GENERIC;
391 }
392
393 if (gitno_connection_data_from_url(&t->connection_data, t->location, s->service_url) < 0)
394 return t->parse_error = PARSE_ERROR_GENERIC;
395
396 /* Set the redirect URL on the stream. This is a transfer of
397 * ownership of the memory. */
398 if (s->redirect_url)
399 git__free(s->redirect_url);
400
401 s->redirect_url = t->location;
402 t->location = NULL;
403
404 t->connected = 0;
405 s->redirect_count++;
406
407 t->parse_error = PARSE_ERROR_REPLAY;
408 return 0;
409 }
410
411 /* Check for a 200 HTTP status code. */
412 if (parser->status_code != 200) {
413 giterr_set(GITERR_NET,
414 "Unexpected HTTP status code: %d",
415 parser->status_code);
416 return t->parse_error = PARSE_ERROR_GENERIC;
417 }
418
419 /* The response must contain a Content-Type header. */
420 if (!t->content_type) {
421 giterr_set(GITERR_NET, "No Content-Type header in response");
422 return t->parse_error = PARSE_ERROR_GENERIC;
423 }
424
425 /* The Content-Type header must match our expectation. */
426 if (get_verb == s->verb)
427 git_buf_printf(&buf,
428 "application/x-git-%s-advertisement",
429 ctx->s->service);
430 else
431 git_buf_printf(&buf,
432 "application/x-git-%s-result",
433 ctx->s->service);
434
435 if (git_buf_oom(&buf))
436 return t->parse_error = PARSE_ERROR_GENERIC;
437
438 if (strcmp(t->content_type, git_buf_cstr(&buf))) {
439 git_buf_free(&buf);
440 giterr_set(GITERR_NET,
441 "Invalid Content-Type: %s",
442 t->content_type);
443 return t->parse_error = PARSE_ERROR_GENERIC;
444 }
445
446 git_buf_free(&buf);
447
448 return 0;
449 }
450
451 static int on_message_complete(http_parser *parser)
452 {
453 parser_context *ctx = (parser_context *) parser->data;
454 http_subtransport *t = ctx->t;
455
456 t->parse_finished = 1;
457
458 return 0;
459 }
460
461 static int on_body_fill_buffer(http_parser *parser, const char *str, size_t len)
462 {
463 parser_context *ctx = (parser_context *) parser->data;
464 http_subtransport *t = ctx->t;
465
466 /* If our goal is to replay the request (either an auth failure or
467 * a redirect) then don't bother buffering since we're ignoring the
468 * content anyway.
469 */
470 if (t->parse_error == PARSE_ERROR_REPLAY)
471 return 0;
472
473 if (ctx->buf_size < len) {
474 giterr_set(GITERR_NET, "Can't fit data in the buffer");
475 return t->parse_error = PARSE_ERROR_GENERIC;
476 }
477
478 memcpy(ctx->buffer, str, len);
479 *(ctx->bytes_read) += len;
480 ctx->buffer += len;
481 ctx->buf_size -= len;
482
483 return 0;
484 }
485
486 static void clear_parser_state(http_subtransport *t)
487 {
488 http_parser_init(&t->parser, HTTP_RESPONSE);
489 gitno_buffer_setup_fromstream(t->io,
490 &t->parse_buffer,
491 t->parse_buffer_data,
492 sizeof(t->parse_buffer_data));
493
494 t->last_cb = NONE;
495 t->parse_error = 0;
496 t->parse_finished = 0;
497
498 git_buf_free(&t->parse_header_name);
499 git_buf_init(&t->parse_header_name, 0);
500
501 git_buf_free(&t->parse_header_value);
502 git_buf_init(&t->parse_header_value, 0);
503
504 git__free(t->content_type);
505 t->content_type = NULL;
506
507 git__free(t->location);
508 t->location = NULL;
509
510 git_vector_free_deep(&t->www_authenticate);
511 }
512
513 static int write_chunk(git_stream *io, const char *buffer, size_t len)
514 {
515 git_buf buf = GIT_BUF_INIT;
516
517 /* Chunk header */
518 git_buf_printf(&buf, "%" PRIxZ "\r\n", len);
519
520 if (git_buf_oom(&buf))
521 return -1;
522
523 if (git_stream_write(io, buf.ptr, buf.size, 0) < 0) {
524 git_buf_free(&buf);
525 return -1;
526 }
527
528 git_buf_free(&buf);
529
530 /* Chunk body */
531 if (len > 0 && git_stream_write(io, buffer, len, 0) < 0)
532 return -1;
533
534 /* Chunk footer */
535 if (git_stream_write(io, "\r\n", 2, 0) < 0)
536 return -1;
537
538 return 0;
539 }
540
541 static int http_connect(http_subtransport *t)
542 {
543 int error;
544 char *proxy_url;
545
546 if (t->connected &&
547 http_should_keep_alive(&t->parser) &&
548 t->parse_finished)
549 return 0;
550
551 if (t->io) {
552 git_stream_close(t->io);
553 git_stream_free(t->io);
554 t->io = NULL;
555 }
556
557 if (t->connection_data.use_ssl) {
558 error = git_tls_stream_new(&t->io, t->connection_data.host, t->connection_data.port);
559 } else {
560 #ifdef GIT_CURL
561 error = git_curl_stream_new(&t->io, t->connection_data.host, t->connection_data.port);
562 #else
563 error = git_socket_stream_new(&t->io, t->connection_data.host, t->connection_data.port);
564 #endif
565 }
566
567 if (error < 0)
568 return error;
569
570 GITERR_CHECK_VERSION(t->io, GIT_STREAM_VERSION, "git_stream");
571
572 if (git_stream_supports_proxy(t->io) &&
573 !git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &proxy_url)) {
574 error = git_stream_set_proxy(t->io, proxy_url);
575 git__free(proxy_url);
576
577 if (error < 0)
578 return error;
579 }
580
581 error = git_stream_connect(t->io);
582
583 #if defined(GIT_OPENSSL) || defined(GIT_SECURE_TRANSPORT) || defined(GIT_CURL)
584 if ((!error || error == GIT_ECERTIFICATE) && t->owner->certificate_check_cb != NULL &&
585 git_stream_is_encrypted(t->io)) {
586 git_cert *cert;
587 int is_valid;
588
589 if ((error = git_stream_certificate(&cert, t->io)) < 0)
590 return error;
591
592 giterr_clear();
593 is_valid = error != GIT_ECERTIFICATE;
594 error = t->owner->certificate_check_cb(cert, is_valid, t->connection_data.host, t->owner->message_cb_payload);
595
596 if (error < 0) {
597 if (!giterr_last())
598 giterr_set(GITERR_NET, "user cancelled certificate check");
599
600 return error;
601 }
602 }
603 #endif
604 if (error < 0)
605 return error;
606
607 t->connected = 1;
608 return 0;
609 }
610
611 static int http_stream_read(
612 git_smart_subtransport_stream *stream,
613 char *buffer,
614 size_t buf_size,
615 size_t *bytes_read)
616 {
617 http_stream *s = (http_stream *)stream;
618 http_subtransport *t = OWNING_SUBTRANSPORT(s);
619 parser_context ctx;
620 size_t bytes_parsed;
621
622 replay:
623 *bytes_read = 0;
624
625 assert(t->connected);
626
627 if (!s->sent_request) {
628 git_buf request = GIT_BUF_INIT;
629
630 clear_parser_state(t);
631
632 if (gen_request(&request, s, 0) < 0)
633 return -1;
634
635 if (git_stream_write(t->io, request.ptr, request.size, 0) < 0) {
636 git_buf_free(&request);
637 return -1;
638 }
639
640 git_buf_free(&request);
641
642 s->sent_request = 1;
643 }
644
645 if (!s->received_response) {
646 if (s->chunked) {
647 assert(s->verb == post_verb);
648
649 /* Flush, if necessary */
650 if (s->chunk_buffer_len > 0 &&
651 write_chunk(t->io, s->chunk_buffer, s->chunk_buffer_len) < 0)
652 return -1;
653
654 s->chunk_buffer_len = 0;
655
656 /* Write the final chunk. */
657 if (git_stream_write(t->io, "0\r\n\r\n", 5, 0) < 0)
658 return -1;
659 }
660
661 s->received_response = 1;
662 }
663
664 while (!*bytes_read && !t->parse_finished) {
665 size_t data_offset;
666 int error;
667
668 /*
669 * Make the parse_buffer think it's as full of data as
670 * the buffer, so it won't try to recv more data than
671 * we can put into it.
672 *
673 * data_offset is the actual data offset from which we
674 * should tell the parser to start reading.
675 */
676 if (buf_size >= t->parse_buffer.len) {
677 t->parse_buffer.offset = 0;
678 } else {
679 t->parse_buffer.offset = t->parse_buffer.len - buf_size;
680 }
681
682 data_offset = t->parse_buffer.offset;
683
684 if (gitno_recv(&t->parse_buffer) < 0)
685 return -1;
686
687 /* This call to http_parser_execute will result in invocations of the
688 * on_* family of callbacks. The most interesting of these is
689 * on_body_fill_buffer, which is called when data is ready to be copied
690 * into the target buffer. We need to marshal the buffer, buf_size, and
691 * bytes_read parameters to this callback. */
692 ctx.t = t;
693 ctx.s = s;
694 ctx.buffer = buffer;
695 ctx.buf_size = buf_size;
696 ctx.bytes_read = bytes_read;
697
698 /* Set the context, call the parser, then unset the context. */
699 t->parser.data = &ctx;
700
701 bytes_parsed = http_parser_execute(&t->parser,
702 &t->settings,
703 t->parse_buffer.data + data_offset,
704 t->parse_buffer.offset - data_offset);
705
706 t->parser.data = NULL;
707
708 /* If there was a handled authentication failure, then parse_error
709 * will have signaled us that we should replay the request. */
710 if (PARSE_ERROR_REPLAY == t->parse_error) {
711 s->sent_request = 0;
712
713 if ((error = http_connect(t)) < 0)
714 return error;
715
716 goto replay;
717 }
718
719 if (t->parse_error == PARSE_ERROR_EXT) {
720 return t->error;
721 }
722
723 if (t->parse_error < 0)
724 return -1;
725
726 if (bytes_parsed != t->parse_buffer.offset - data_offset) {
727 giterr_set(GITERR_NET,
728 "HTTP parser error: %s",
729 http_errno_description((enum http_errno)t->parser.http_errno));
730 return -1;
731 }
732 }
733
734 return 0;
735 }
736
737 static int http_stream_write_chunked(
738 git_smart_subtransport_stream *stream,
739 const char *buffer,
740 size_t len)
741 {
742 http_stream *s = (http_stream *)stream;
743 http_subtransport *t = OWNING_SUBTRANSPORT(s);
744
745 assert(t->connected);
746
747 /* Send the request, if necessary */
748 if (!s->sent_request) {
749 git_buf request = GIT_BUF_INIT;
750
751 clear_parser_state(t);
752
753 if (gen_request(&request, s, 0) < 0)
754 return -1;
755
756 if (git_stream_write(t->io, request.ptr, request.size, 0) < 0) {
757 git_buf_free(&request);
758 return -1;
759 }
760
761 git_buf_free(&request);
762
763 s->sent_request = 1;
764 }
765
766 if (len > CHUNK_SIZE) {
767 /* Flush, if necessary */
768 if (s->chunk_buffer_len > 0) {
769 if (write_chunk(t->io, s->chunk_buffer, s->chunk_buffer_len) < 0)
770 return -1;
771
772 s->chunk_buffer_len = 0;
773 }
774
775 /* Write chunk directly */
776 if (write_chunk(t->io, buffer, len) < 0)
777 return -1;
778 }
779 else {
780 /* Append as much to the buffer as we can */
781 int count = min(CHUNK_SIZE - s->chunk_buffer_len, len);
782
783 if (!s->chunk_buffer)
784 s->chunk_buffer = git__malloc(CHUNK_SIZE);
785
786 memcpy(s->chunk_buffer + s->chunk_buffer_len, buffer, count);
787 s->chunk_buffer_len += count;
788 buffer += count;
789 len -= count;
790
791 /* Is the buffer full? If so, then flush */
792 if (CHUNK_SIZE == s->chunk_buffer_len) {
793 if (write_chunk(t->io, s->chunk_buffer, s->chunk_buffer_len) < 0)
794 return -1;
795
796 s->chunk_buffer_len = 0;
797
798 if (len > 0) {
799 memcpy(s->chunk_buffer, buffer, len);
800 s->chunk_buffer_len = len;
801 }
802 }
803 }
804
805 return 0;
806 }
807
808 static int http_stream_write_single(
809 git_smart_subtransport_stream *stream,
810 const char *buffer,
811 size_t len)
812 {
813 http_stream *s = (http_stream *)stream;
814 http_subtransport *t = OWNING_SUBTRANSPORT(s);
815 git_buf request = GIT_BUF_INIT;
816
817 assert(t->connected);
818
819 if (s->sent_request) {
820 giterr_set(GITERR_NET, "Subtransport configured for only one write");
821 return -1;
822 }
823
824 clear_parser_state(t);
825
826 if (gen_request(&request, s, len) < 0)
827 return -1;
828
829 if (git_stream_write(t->io, request.ptr, request.size, 0) < 0)
830 goto on_error;
831
832 if (len && git_stream_write(t->io, buffer, len, 0) < 0)
833 goto on_error;
834
835 git_buf_free(&request);
836 s->sent_request = 1;
837
838 return 0;
839
840 on_error:
841 git_buf_free(&request);
842 return -1;
843 }
844
845 static void http_stream_free(git_smart_subtransport_stream *stream)
846 {
847 http_stream *s = (http_stream *)stream;
848
849 if (s->chunk_buffer)
850 git__free(s->chunk_buffer);
851
852 if (s->redirect_url)
853 git__free(s->redirect_url);
854
855 git__free(s);
856 }
857
858 static int http_stream_alloc(http_subtransport *t,
859 git_smart_subtransport_stream **stream)
860 {
861 http_stream *s;
862
863 if (!stream)
864 return -1;
865
866 s = git__calloc(sizeof(http_stream), 1);
867 GITERR_CHECK_ALLOC(s);
868
869 s->parent.subtransport = &t->parent;
870 s->parent.read = http_stream_read;
871 s->parent.write = http_stream_write_single;
872 s->parent.free = http_stream_free;
873
874 *stream = (git_smart_subtransport_stream *)s;
875 return 0;
876 }
877
878 static int http_uploadpack_ls(
879 http_subtransport *t,
880 git_smart_subtransport_stream **stream)
881 {
882 http_stream *s;
883
884 if (http_stream_alloc(t, stream) < 0)
885 return -1;
886
887 s = (http_stream *)*stream;
888
889 s->service = upload_pack_service;
890 s->service_url = upload_pack_ls_service_url;
891 s->verb = get_verb;
892
893 return 0;
894 }
895
896 static int http_uploadpack(
897 http_subtransport *t,
898 git_smart_subtransport_stream **stream)
899 {
900 http_stream *s;
901
902 if (http_stream_alloc(t, stream) < 0)
903 return -1;
904
905 s = (http_stream *)*stream;
906
907 s->service = upload_pack_service;
908 s->service_url = upload_pack_service_url;
909 s->verb = post_verb;
910
911 return 0;
912 }
913
914 static int http_receivepack_ls(
915 http_subtransport *t,
916 git_smart_subtransport_stream **stream)
917 {
918 http_stream *s;
919
920 if (http_stream_alloc(t, stream) < 0)
921 return -1;
922
923 s = (http_stream *)*stream;
924
925 s->service = receive_pack_service;
926 s->service_url = receive_pack_ls_service_url;
927 s->verb = get_verb;
928
929 return 0;
930 }
931
932 static int http_receivepack(
933 http_subtransport *t,
934 git_smart_subtransport_stream **stream)
935 {
936 http_stream *s;
937
938 if (http_stream_alloc(t, stream) < 0)
939 return -1;
940
941 s = (http_stream *)*stream;
942
943 /* Use Transfer-Encoding: chunked for this request */
944 s->chunked = 1;
945 s->parent.write = http_stream_write_chunked;
946
947 s->service = receive_pack_service;
948 s->service_url = receive_pack_service_url;
949 s->verb = post_verb;
950
951 return 0;
952 }
953
954 static int http_action(
955 git_smart_subtransport_stream **stream,
956 git_smart_subtransport *subtransport,
957 const char *url,
958 git_smart_service_t action)
959 {
960 http_subtransport *t = (http_subtransport *)subtransport;
961 int ret;
962
963 if (!stream)
964 return -1;
965
966 if ((!t->connection_data.host || !t->connection_data.port || !t->connection_data.path) &&
967 (ret = gitno_connection_data_from_url(&t->connection_data, url, NULL)) < 0)
968 return ret;
969
970 if ((ret = http_connect(t)) < 0)
971 return ret;
972
973 switch (action) {
974 case GIT_SERVICE_UPLOADPACK_LS:
975 return http_uploadpack_ls(t, stream);
976
977 case GIT_SERVICE_UPLOADPACK:
978 return http_uploadpack(t, stream);
979
980 case GIT_SERVICE_RECEIVEPACK_LS:
981 return http_receivepack_ls(t, stream);
982
983 case GIT_SERVICE_RECEIVEPACK:
984 return http_receivepack(t, stream);
985 }
986
987 *stream = NULL;
988 return -1;
989 }
990
991 static int http_close(git_smart_subtransport *subtransport)
992 {
993 http_subtransport *t = (http_subtransport *) subtransport;
994 git_http_auth_context *context;
995 size_t i;
996
997 clear_parser_state(t);
998
999 if (t->io) {
1000 git_stream_close(t->io);
1001 git_stream_free(t->io);
1002 t->io = NULL;
1003 }
1004
1005 if (t->cred) {
1006 t->cred->free(t->cred);
1007 t->cred = NULL;
1008 }
1009
1010 if (t->url_cred) {
1011 t->url_cred->free(t->url_cred);
1012 t->url_cred = NULL;
1013 }
1014
1015 git_vector_foreach(&t->auth_contexts, i, context) {
1016 if (context->free)
1017 context->free(context);
1018 }
1019
1020 git_vector_clear(&t->auth_contexts);
1021
1022 gitno_connection_data_free_ptrs(&t->connection_data);
1023 memset(&t->connection_data, 0x0, sizeof(gitno_connection_data));
1024
1025 return 0;
1026 }
1027
1028 static void http_free(git_smart_subtransport *subtransport)
1029 {
1030 http_subtransport *t = (http_subtransport *) subtransport;
1031
1032 http_close(subtransport);
1033
1034 git_vector_free(&t->auth_contexts);
1035 git__free(t);
1036 }
1037
1038 int git_smart_subtransport_http(git_smart_subtransport **out, git_transport *owner, void *param)
1039 {
1040 http_subtransport *t;
1041
1042 GIT_UNUSED(param);
1043
1044 if (!out)
1045 return -1;
1046
1047 t = git__calloc(sizeof(http_subtransport), 1);
1048 GITERR_CHECK_ALLOC(t);
1049
1050 t->owner = (transport_smart *)owner;
1051 t->parent.action = http_action;
1052 t->parent.close = http_close;
1053 t->parent.free = http_free;
1054
1055 t->settings.on_header_field = on_header_field;
1056 t->settings.on_header_value = on_header_value;
1057 t->settings.on_headers_complete = on_headers_complete;
1058 t->settings.on_body = on_body_fill_buffer;
1059 t->settings.on_message_complete = on_message_complete;
1060
1061 *out = (git_smart_subtransport *) t;
1062 return 0;
1063 }
1064
1065 #endif /* !GIT_WINHTTP */