2 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
10 #include "http_parser.h"
17 #include "auth_negotiate.h"
18 #include "tls_stream.h"
19 #include "socket_stream.h"
20 #include "curl_stream.h"
22 git_http_auth_scheme auth_schemes
[] = {
23 { GIT_AUTHTYPE_NEGOTIATE
, "Negotiate", GIT_CREDTYPE_DEFAULT
, git_http_auth_negotiate
},
24 { GIT_AUTHTYPE_BASIC
, "Basic", GIT_CREDTYPE_USERPASS_PLAINTEXT
, git_http_auth_basic
},
27 static const char *upload_pack_service
= "upload-pack";
28 static const char *upload_pack_ls_service_url
= "/info/refs?service=git-upload-pack";
29 static const char *upload_pack_service_url
= "/git-upload-pack";
30 static const char *receive_pack_service
= "receive-pack";
31 static const char *receive_pack_ls_service_url
= "/info/refs?service=git-receive-pack";
32 static const char *receive_pack_service_url
= "/git-receive-pack";
33 static const char *get_verb
= "GET";
34 static const char *post_verb
= "POST";
36 #define OWNING_SUBTRANSPORT(s) ((http_subtransport *)(s)->parent.subtransport)
38 #define PARSE_ERROR_GENERIC -1
39 #define PARSE_ERROR_REPLAY -2
40 /** Look at the user field */
41 #define PARSE_ERROR_EXT -3
43 #define CHUNK_SIZE 4096
52 git_smart_subtransport_stream parent
;
54 const char *service_url
;
58 unsigned chunk_buffer_len
;
59 unsigned sent_request
: 1,
60 received_response
: 1,
66 git_smart_subtransport parent
;
67 transport_smart
*owner
;
69 gitno_connection_data connection_data
;
72 /* Parser structures */
74 http_parser_settings settings
;
75 gitno_buffer parse_buffer
;
76 git_buf parse_header_name
;
77 git_buf parse_header_value
;
78 char parse_buffer_data
[NETIO_BUFSIZE
];
81 git_vector www_authenticate
;
85 unsigned parse_finished
: 1;
90 git_vector auth_contexts
;
97 /* Target buffer details from read() */
103 static bool credtype_match(git_http_auth_scheme
*scheme
, void *data
)
105 unsigned int credtype
= *(unsigned int *)data
;
107 return !!(scheme
->credtypes
& credtype
);
110 static bool challenge_match(git_http_auth_scheme
*scheme
, void *data
)
112 const char *scheme_name
= scheme
->name
;
113 const char *challenge
= (const char *)data
;
116 scheme_len
= strlen(scheme_name
);
117 return (strncasecmp(challenge
, scheme_name
, scheme_len
) == 0 &&
118 (challenge
[scheme_len
] == '\0' || challenge
[scheme_len
] == ' '));
121 static int auth_context_match(
122 git_http_auth_context
**out
,
123 http_subtransport
*t
,
124 bool (*scheme_match
)(git_http_auth_scheme
*scheme
, void *data
),
127 git_http_auth_scheme
*scheme
= NULL
;
128 git_http_auth_context
*context
= NULL
, *c
;
133 for (i
= 0; i
< ARRAY_SIZE(auth_schemes
); i
++) {
134 if (scheme_match(&auth_schemes
[i
], data
)) {
135 scheme
= &auth_schemes
[i
];
143 /* See if authentication has already started for this scheme */
144 git_vector_foreach(&t
->auth_contexts
, i
, c
) {
145 if (c
->type
== scheme
->type
) {
152 if (scheme
->init_context(&context
, &t
->connection_data
) < 0)
156 else if (git_vector_insert(&t
->auth_contexts
, context
) < 0)
165 static int apply_credentials(git_buf
*buf
, http_subtransport
*t
)
167 git_cred
*cred
= t
->cred
;
168 git_http_auth_context
*context
;
170 /* Apply the credentials given to us in the URL */
171 if (!cred
&& t
->connection_data
.user
&& t
->connection_data
.pass
) {
173 git_cred_userpass_plaintext_new(&t
->url_cred
,
174 t
->connection_data
.user
, t
->connection_data
.pass
) < 0)
183 /* Get or create a context for the best scheme for this cred type */
184 if (auth_context_match(&context
, t
, credtype_match
, &cred
->credtype
) < 0)
187 return context
->next_token(buf
, context
, cred
);
190 static const char *user_agent(void)
192 const char *custom
= git_libgit2__user_agent();
197 return "libgit2 " LIBGIT2_VERSION
;
200 static int gen_request(
203 size_t content_length
)
205 http_subtransport
*t
= OWNING_SUBTRANSPORT(s
);
206 const char *path
= t
->connection_data
.path
? t
->connection_data
.path
: "/";
209 git_buf_printf(buf
, "%s %s%s HTTP/1.1\r\n", s
->verb
, path
, s
->service_url
);
211 git_buf_printf(buf
, "User-Agent: git/2.0 (%s)\r\n", user_agent());
212 git_buf_printf(buf
, "Host: %s\r\n", t
->connection_data
.host
);
214 if (s
->chunked
|| content_length
> 0) {
215 git_buf_printf(buf
, "Accept: application/x-git-%s-result\r\n", s
->service
);
216 git_buf_printf(buf
, "Content-Type: application/x-git-%s-request\r\n", s
->service
);
219 git_buf_puts(buf
, "Transfer-Encoding: chunked\r\n");
221 git_buf_printf(buf
, "Content-Length: %"PRIuZ
"\r\n", content_length
);
223 git_buf_puts(buf
, "Accept: */*\r\n");
225 for (i
= 0; i
< t
->owner
->custom_headers
.count
; i
++) {
226 if (t
->owner
->custom_headers
.strings
[i
])
227 git_buf_printf(buf
, "%s\r\n", t
->owner
->custom_headers
.strings
[i
]);
230 /* Apply credentials to the request */
231 if (apply_credentials(buf
, t
) < 0)
234 git_buf_puts(buf
, "\r\n");
236 if (git_buf_oom(buf
))
242 static int parse_authenticate_response(
243 git_vector
*www_authenticate
,
244 http_subtransport
*t
,
247 git_http_auth_context
*context
;
251 git_vector_foreach(www_authenticate
, i
, challenge
) {
252 if (auth_context_match(&context
, t
, challenge_match
, challenge
) < 0)
257 if (context
->set_challenge
&&
258 context
->set_challenge(context
, challenge
) < 0)
261 *allowed_types
|= context
->credtypes
;
267 static int on_header_ready(http_subtransport
*t
)
269 git_buf
*name
= &t
->parse_header_name
;
270 git_buf
*value
= &t
->parse_header_value
;
272 if (!strcasecmp("Content-Type", git_buf_cstr(name
))) {
273 if (!t
->content_type
) {
274 t
->content_type
= git__strdup(git_buf_cstr(value
));
275 GITERR_CHECK_ALLOC(t
->content_type
);
278 else if (!strcasecmp("WWW-Authenticate", git_buf_cstr(name
))) {
279 char *dup
= git__strdup(git_buf_cstr(value
));
280 GITERR_CHECK_ALLOC(dup
);
282 git_vector_insert(&t
->www_authenticate
, dup
);
284 else if (!strcasecmp("Location", git_buf_cstr(name
))) {
286 t
->location
= git__strdup(git_buf_cstr(value
));
287 GITERR_CHECK_ALLOC(t
->location
);
294 static int on_header_field(http_parser
*parser
, const char *str
, size_t len
)
296 parser_context
*ctx
= (parser_context
*) parser
->data
;
297 http_subtransport
*t
= ctx
->t
;
299 /* Both parse_header_name and parse_header_value are populated
300 * and ready for consumption */
301 if (VALUE
== t
->last_cb
)
302 if (on_header_ready(t
) < 0)
303 return t
->parse_error
= PARSE_ERROR_GENERIC
;
305 if (NONE
== t
->last_cb
|| VALUE
== t
->last_cb
)
306 git_buf_clear(&t
->parse_header_name
);
308 if (git_buf_put(&t
->parse_header_name
, str
, len
) < 0)
309 return t
->parse_error
= PARSE_ERROR_GENERIC
;
315 static int on_header_value(http_parser
*parser
, const char *str
, size_t len
)
317 parser_context
*ctx
= (parser_context
*) parser
->data
;
318 http_subtransport
*t
= ctx
->t
;
320 assert(NONE
!= t
->last_cb
);
322 if (FIELD
== t
->last_cb
)
323 git_buf_clear(&t
->parse_header_value
);
325 if (git_buf_put(&t
->parse_header_value
, str
, len
) < 0)
326 return t
->parse_error
= PARSE_ERROR_GENERIC
;
332 static int on_headers_complete(http_parser
*parser
)
334 parser_context
*ctx
= (parser_context
*) parser
->data
;
335 http_subtransport
*t
= ctx
->t
;
336 http_stream
*s
= ctx
->s
;
337 git_buf buf
= GIT_BUF_INIT
;
338 int error
= 0, no_callback
= 0, allowed_auth_types
= 0;
340 /* Both parse_header_name and parse_header_value are populated
341 * and ready for consumption. */
342 if (VALUE
== t
->last_cb
)
343 if (on_header_ready(t
) < 0)
344 return t
->parse_error
= PARSE_ERROR_GENERIC
;
346 /* Capture authentication headers which may be a 401 (authentication
347 * is not complete) or a 200 (simply informing us that auth *is*
350 if (parse_authenticate_response(&t
->www_authenticate
, t
,
351 &allowed_auth_types
) < 0)
352 return t
->parse_error
= PARSE_ERROR_GENERIC
;
354 /* Check for an authentication failure. */
355 if (parser
->status_code
== 401 && get_verb
== s
->verb
) {
356 if (!t
->owner
->cred_acquire_cb
) {
359 if (allowed_auth_types
) {
361 t
->cred
->free(t
->cred
);
365 error
= t
->owner
->cred_acquire_cb(&t
->cred
,
367 t
->connection_data
.user
,
369 t
->owner
->cred_acquire_payload
);
371 if (error
== GIT_PASSTHROUGH
) {
373 } else if (error
< 0) {
375 return t
->parse_error
= PARSE_ERROR_EXT
;
379 if (!(t
->cred
->credtype
& allowed_auth_types
)) {
380 giterr_set(GITERR_NET
, "credentials callback returned an invalid cred type");
381 return t
->parse_error
= PARSE_ERROR_GENERIC
;
384 /* Successfully acquired a credential. */
385 t
->parse_error
= PARSE_ERROR_REPLAY
;
392 giterr_set(GITERR_NET
, "authentication required but no callback set");
393 return t
->parse_error
= PARSE_ERROR_GENERIC
;
397 /* Check for a redirect.
398 * Right now we only permit a redirect to the same hostname. */
399 if ((parser
->status_code
== 301 ||
400 parser
->status_code
== 302 ||
401 (parser
->status_code
== 303 && get_verb
== s
->verb
) ||
402 parser
->status_code
== 307) &&
405 if (s
->redirect_count
>= 7) {
406 giterr_set(GITERR_NET
, "too many redirects");
407 return t
->parse_error
= PARSE_ERROR_GENERIC
;
410 if (gitno_connection_data_from_url(&t
->connection_data
, t
->location
, s
->service_url
) < 0)
411 return t
->parse_error
= PARSE_ERROR_GENERIC
;
413 /* Set the redirect URL on the stream. This is a transfer of
414 * ownership of the memory. */
416 git__free(s
->redirect_url
);
418 s
->redirect_url
= t
->location
;
424 t
->parse_error
= PARSE_ERROR_REPLAY
;
428 /* Check for a 200 HTTP status code. */
429 if (parser
->status_code
!= 200) {
430 giterr_set(GITERR_NET
,
431 "unexpected HTTP status code: %d",
432 parser
->status_code
);
433 return t
->parse_error
= PARSE_ERROR_GENERIC
;
436 /* The response must contain a Content-Type header. */
437 if (!t
->content_type
) {
438 giterr_set(GITERR_NET
, "no Content-Type header in response");
439 return t
->parse_error
= PARSE_ERROR_GENERIC
;
442 /* The Content-Type header must match our expectation. */
443 if (get_verb
== s
->verb
)
445 "application/x-git-%s-advertisement",
449 "application/x-git-%s-result",
452 if (git_buf_oom(&buf
))
453 return t
->parse_error
= PARSE_ERROR_GENERIC
;
455 if (strcmp(t
->content_type
, git_buf_cstr(&buf
))) {
457 giterr_set(GITERR_NET
,
458 "invalid Content-Type: %s",
460 return t
->parse_error
= PARSE_ERROR_GENERIC
;
468 static int on_message_complete(http_parser
*parser
)
470 parser_context
*ctx
= (parser_context
*) parser
->data
;
471 http_subtransport
*t
= ctx
->t
;
473 t
->parse_finished
= 1;
478 static int on_body_fill_buffer(http_parser
*parser
, const char *str
, size_t len
)
480 parser_context
*ctx
= (parser_context
*) parser
->data
;
481 http_subtransport
*t
= ctx
->t
;
483 /* If our goal is to replay the request (either an auth failure or
484 * a redirect) then don't bother buffering since we're ignoring the
487 if (t
->parse_error
== PARSE_ERROR_REPLAY
)
490 if (ctx
->buf_size
< len
) {
491 giterr_set(GITERR_NET
, "can't fit data in the buffer");
492 return t
->parse_error
= PARSE_ERROR_GENERIC
;
495 memcpy(ctx
->buffer
, str
, len
);
496 *(ctx
->bytes_read
) += len
;
498 ctx
->buf_size
-= len
;
503 static void clear_parser_state(http_subtransport
*t
)
505 http_parser_init(&t
->parser
, HTTP_RESPONSE
);
506 gitno_buffer_setup_fromstream(t
->io
,
508 t
->parse_buffer_data
,
509 sizeof(t
->parse_buffer_data
));
513 t
->parse_finished
= 0;
515 git_buf_free(&t
->parse_header_name
);
516 git_buf_init(&t
->parse_header_name
, 0);
518 git_buf_free(&t
->parse_header_value
);
519 git_buf_init(&t
->parse_header_value
, 0);
521 git__free(t
->content_type
);
522 t
->content_type
= NULL
;
524 git__free(t
->location
);
527 git_vector_free_deep(&t
->www_authenticate
);
530 static int write_chunk(git_stream
*io
, const char *buffer
, size_t len
)
532 git_buf buf
= GIT_BUF_INIT
;
535 git_buf_printf(&buf
, "%" PRIxZ
"\r\n", len
);
537 if (git_buf_oom(&buf
))
540 if (git_stream_write(io
, buf
.ptr
, buf
.size
, 0) < 0) {
548 if (len
> 0 && git_stream_write(io
, buffer
, len
, 0) < 0)
552 if (git_stream_write(io
, "\r\n", 2, 0) < 0)
558 static int apply_proxy_config(http_subtransport
*t
)
561 git_proxy_t proxy_type
;
563 if (!git_stream_supports_proxy(t
->io
))
566 proxy_type
= t
->owner
->proxy
.type
;
568 if (proxy_type
== GIT_PROXY_NONE
)
571 if (proxy_type
== GIT_PROXY_AUTO
) {
573 git_proxy_options opts
= GIT_PROXY_OPTIONS_INIT
;
575 if ((error
= git_remote__get_http_proxy(t
->owner
->owner
, !!t
->connection_data
.use_ssl
, &url
)) < 0)
578 opts
.credentials
= t
->owner
->proxy
.credentials
;
579 opts
.certificate_check
= t
->owner
->proxy
.certificate_check
;
580 opts
.payload
= t
->owner
->proxy
.payload
;
581 opts
.type
= GIT_PROXY_SPECIFIED
;
583 error
= git_stream_set_proxy(t
->io
, &opts
);
589 return git_stream_set_proxy(t
->io
, &t
->owner
->proxy
);
592 static int http_connect(http_subtransport
*t
)
597 http_should_keep_alive(&t
->parser
) &&
602 git_stream_close(t
->io
);
603 git_stream_free(t
->io
);
608 if (t
->connection_data
.use_ssl
) {
609 error
= git_tls_stream_new(&t
->io
, t
->connection_data
.host
, t
->connection_data
.port
);
612 error
= git_curl_stream_new(&t
->io
, t
->connection_data
.host
, t
->connection_data
.port
);
614 error
= git_socket_stream_new(&t
->io
, t
->connection_data
.host
, t
->connection_data
.port
);
621 GITERR_CHECK_VERSION(t
->io
, GIT_STREAM_VERSION
, "git_stream");
623 apply_proxy_config(t
);
625 error
= git_stream_connect(t
->io
);
627 if ((!error
|| error
== GIT_ECERTIFICATE
) && t
->owner
->certificate_check_cb
!= NULL
&&
628 git_stream_is_encrypted(t
->io
)) {
630 int is_valid
= (error
== GIT_OK
);
632 if ((error
= git_stream_certificate(&cert
, t
->io
)) < 0)
636 error
= t
->owner
->certificate_check_cb(cert
, is_valid
, t
->connection_data
.host
, t
->owner
->message_cb_payload
);
640 giterr_set(GITERR_NET
, "user cancelled certificate check");
653 static int http_stream_read(
654 git_smart_subtransport_stream
*stream
,
659 http_stream
*s
= (http_stream
*)stream
;
660 http_subtransport
*t
= OWNING_SUBTRANSPORT(s
);
667 assert(t
->connected
);
669 if (!s
->sent_request
) {
670 git_buf request
= GIT_BUF_INIT
;
672 clear_parser_state(t
);
674 if (gen_request(&request
, s
, 0) < 0)
677 if (git_stream_write(t
->io
, request
.ptr
, request
.size
, 0) < 0) {
678 git_buf_free(&request
);
682 git_buf_free(&request
);
687 if (!s
->received_response
) {
689 assert(s
->verb
== post_verb
);
691 /* Flush, if necessary */
692 if (s
->chunk_buffer_len
> 0 &&
693 write_chunk(t
->io
, s
->chunk_buffer
, s
->chunk_buffer_len
) < 0)
696 s
->chunk_buffer_len
= 0;
698 /* Write the final chunk. */
699 if (git_stream_write(t
->io
, "0\r\n\r\n", 5, 0) < 0)
703 s
->received_response
= 1;
706 while (!*bytes_read
&& !t
->parse_finished
) {
711 * Make the parse_buffer think it's as full of data as
712 * the buffer, so it won't try to recv more data than
713 * we can put into it.
715 * data_offset is the actual data offset from which we
716 * should tell the parser to start reading.
718 if (buf_size
>= t
->parse_buffer
.len
) {
719 t
->parse_buffer
.offset
= 0;
721 t
->parse_buffer
.offset
= t
->parse_buffer
.len
- buf_size
;
724 data_offset
= t
->parse_buffer
.offset
;
726 if (gitno_recv(&t
->parse_buffer
) < 0)
729 /* This call to http_parser_execute will result in invocations of the
730 * on_* family of callbacks. The most interesting of these is
731 * on_body_fill_buffer, which is called when data is ready to be copied
732 * into the target buffer. We need to marshal the buffer, buf_size, and
733 * bytes_read parameters to this callback. */
737 ctx
.buf_size
= buf_size
;
738 ctx
.bytes_read
= bytes_read
;
740 /* Set the context, call the parser, then unset the context. */
741 t
->parser
.data
= &ctx
;
743 bytes_parsed
= http_parser_execute(&t
->parser
,
745 t
->parse_buffer
.data
+ data_offset
,
746 t
->parse_buffer
.offset
- data_offset
);
748 t
->parser
.data
= NULL
;
750 /* If there was a handled authentication failure, then parse_error
751 * will have signaled us that we should replay the request. */
752 if (PARSE_ERROR_REPLAY
== t
->parse_error
) {
755 if ((error
= http_connect(t
)) < 0)
761 if (t
->parse_error
== PARSE_ERROR_EXT
) {
765 if (t
->parse_error
< 0)
768 if (bytes_parsed
!= t
->parse_buffer
.offset
- data_offset
) {
769 giterr_set(GITERR_NET
,
770 "HTTP parser error: %s",
771 http_errno_description((enum http_errno
)t
->parser
.http_errno
));
779 static int http_stream_write_chunked(
780 git_smart_subtransport_stream
*stream
,
784 http_stream
*s
= (http_stream
*)stream
;
785 http_subtransport
*t
= OWNING_SUBTRANSPORT(s
);
787 assert(t
->connected
);
789 /* Send the request, if necessary */
790 if (!s
->sent_request
) {
791 git_buf request
= GIT_BUF_INIT
;
793 clear_parser_state(t
);
795 if (gen_request(&request
, s
, 0) < 0)
798 if (git_stream_write(t
->io
, request
.ptr
, request
.size
, 0) < 0) {
799 git_buf_free(&request
);
803 git_buf_free(&request
);
808 if (len
> CHUNK_SIZE
) {
809 /* Flush, if necessary */
810 if (s
->chunk_buffer_len
> 0) {
811 if (write_chunk(t
->io
, s
->chunk_buffer
, s
->chunk_buffer_len
) < 0)
814 s
->chunk_buffer_len
= 0;
817 /* Write chunk directly */
818 if (write_chunk(t
->io
, buffer
, len
) < 0)
822 /* Append as much to the buffer as we can */
823 int count
= min(CHUNK_SIZE
- s
->chunk_buffer_len
, len
);
825 if (!s
->chunk_buffer
)
826 s
->chunk_buffer
= git__malloc(CHUNK_SIZE
);
828 memcpy(s
->chunk_buffer
+ s
->chunk_buffer_len
, buffer
, count
);
829 s
->chunk_buffer_len
+= count
;
833 /* Is the buffer full? If so, then flush */
834 if (CHUNK_SIZE
== s
->chunk_buffer_len
) {
835 if (write_chunk(t
->io
, s
->chunk_buffer
, s
->chunk_buffer_len
) < 0)
838 s
->chunk_buffer_len
= 0;
841 memcpy(s
->chunk_buffer
, buffer
, len
);
842 s
->chunk_buffer_len
= len
;
850 static int http_stream_write_single(
851 git_smart_subtransport_stream
*stream
,
855 http_stream
*s
= (http_stream
*)stream
;
856 http_subtransport
*t
= OWNING_SUBTRANSPORT(s
);
857 git_buf request
= GIT_BUF_INIT
;
859 assert(t
->connected
);
861 if (s
->sent_request
) {
862 giterr_set(GITERR_NET
, "subtransport configured for only one write");
866 clear_parser_state(t
);
868 if (gen_request(&request
, s
, len
) < 0)
871 if (git_stream_write(t
->io
, request
.ptr
, request
.size
, 0) < 0)
874 if (len
&& git_stream_write(t
->io
, buffer
, len
, 0) < 0)
877 git_buf_free(&request
);
883 git_buf_free(&request
);
887 static void http_stream_free(git_smart_subtransport_stream
*stream
)
889 http_stream
*s
= (http_stream
*)stream
;
892 git__free(s
->chunk_buffer
);
895 git__free(s
->redirect_url
);
900 static int http_stream_alloc(http_subtransport
*t
,
901 git_smart_subtransport_stream
**stream
)
908 s
= git__calloc(sizeof(http_stream
), 1);
909 GITERR_CHECK_ALLOC(s
);
911 s
->parent
.subtransport
= &t
->parent
;
912 s
->parent
.read
= http_stream_read
;
913 s
->parent
.write
= http_stream_write_single
;
914 s
->parent
.free
= http_stream_free
;
916 *stream
= (git_smart_subtransport_stream
*)s
;
920 static int http_uploadpack_ls(
921 http_subtransport
*t
,
922 git_smart_subtransport_stream
**stream
)
926 if (http_stream_alloc(t
, stream
) < 0)
929 s
= (http_stream
*)*stream
;
931 s
->service
= upload_pack_service
;
932 s
->service_url
= upload_pack_ls_service_url
;
938 static int http_uploadpack(
939 http_subtransport
*t
,
940 git_smart_subtransport_stream
**stream
)
944 if (http_stream_alloc(t
, stream
) < 0)
947 s
= (http_stream
*)*stream
;
949 s
->service
= upload_pack_service
;
950 s
->service_url
= upload_pack_service_url
;
956 static int http_receivepack_ls(
957 http_subtransport
*t
,
958 git_smart_subtransport_stream
**stream
)
962 if (http_stream_alloc(t
, stream
) < 0)
965 s
= (http_stream
*)*stream
;
967 s
->service
= receive_pack_service
;
968 s
->service_url
= receive_pack_ls_service_url
;
974 static int http_receivepack(
975 http_subtransport
*t
,
976 git_smart_subtransport_stream
**stream
)
980 if (http_stream_alloc(t
, stream
) < 0)
983 s
= (http_stream
*)*stream
;
985 /* Use Transfer-Encoding: chunked for this request */
987 s
->parent
.write
= http_stream_write_chunked
;
989 s
->service
= receive_pack_service
;
990 s
->service_url
= receive_pack_service_url
;
996 static int http_action(
997 git_smart_subtransport_stream
**stream
,
998 git_smart_subtransport
*subtransport
,
1000 git_smart_service_t action
)
1002 http_subtransport
*t
= (http_subtransport
*)subtransport
;
1008 if ((!t
->connection_data
.host
|| !t
->connection_data
.port
|| !t
->connection_data
.path
) &&
1009 (ret
= gitno_connection_data_from_url(&t
->connection_data
, url
, NULL
)) < 0)
1012 if ((ret
= http_connect(t
)) < 0)
1016 case GIT_SERVICE_UPLOADPACK_LS
:
1017 return http_uploadpack_ls(t
, stream
);
1019 case GIT_SERVICE_UPLOADPACK
:
1020 return http_uploadpack(t
, stream
);
1022 case GIT_SERVICE_RECEIVEPACK_LS
:
1023 return http_receivepack_ls(t
, stream
);
1025 case GIT_SERVICE_RECEIVEPACK
:
1026 return http_receivepack(t
, stream
);
1033 static int http_close(git_smart_subtransport
*subtransport
)
1035 http_subtransport
*t
= (http_subtransport
*) subtransport
;
1036 git_http_auth_context
*context
;
1039 clear_parser_state(t
);
1044 git_stream_close(t
->io
);
1045 git_stream_free(t
->io
);
1050 t
->cred
->free(t
->cred
);
1055 t
->url_cred
->free(t
->url_cred
);
1059 git_vector_foreach(&t
->auth_contexts
, i
, context
) {
1061 context
->free(context
);
1064 git_vector_clear(&t
->auth_contexts
);
1066 gitno_connection_data_free_ptrs(&t
->connection_data
);
1067 memset(&t
->connection_data
, 0x0, sizeof(gitno_connection_data
));
1072 static void http_free(git_smart_subtransport
*subtransport
)
1074 http_subtransport
*t
= (http_subtransport
*) subtransport
;
1076 http_close(subtransport
);
1078 git_vector_free(&t
->auth_contexts
);
1082 int git_smart_subtransport_http(git_smart_subtransport
**out
, git_transport
*owner
, void *param
)
1084 http_subtransport
*t
;
1091 t
= git__calloc(sizeof(http_subtransport
), 1);
1092 GITERR_CHECK_ALLOC(t
);
1094 t
->owner
= (transport_smart
*)owner
;
1095 t
->parent
.action
= http_action
;
1096 t
->parent
.close
= http_close
;
1097 t
->parent
.free
= http_free
;
1099 t
->settings
.on_header_field
= on_header_field
;
1100 t
->settings
.on_header_value
= on_header_value
;
1101 t
->settings
.on_headers_complete
= on_headers_complete
;
1102 t
->settings
.on_body
= on_body_fill_buffer
;
1103 t
->settings
.on_message_complete
= on_message_complete
;
1105 *out
= (git_smart_subtransport
*) t
;
1109 #endif /* !GIT_WINHTTP */