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"
16 #include "auth_negotiate.h"
17 #include "tls_stream.h"
18 #include "socket_stream.h"
19 #include "curl_stream.h"
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
},
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";
35 #define OWNING_SUBTRANSPORT(s) ((http_subtransport *)(s)->parent.subtransport)
37 #define PARSE_ERROR_GENERIC -1
38 #define PARSE_ERROR_REPLAY -2
40 #define CHUNK_SIZE 4096
49 git_smart_subtransport_stream parent
;
51 const char *service_url
;
55 unsigned chunk_buffer_len
;
56 unsigned sent_request
: 1,
57 received_response
: 1,
63 git_smart_subtransport parent
;
64 transport_smart
*owner
;
66 gitno_connection_data connection_data
;
69 /* Parser structures */
71 http_parser_settings settings
;
72 gitno_buffer parse_buffer
;
73 git_buf parse_header_name
;
74 git_buf parse_header_value
;
75 char parse_buffer_data
[NETIO_BUFSIZE
];
78 git_vector www_authenticate
;
81 unsigned parse_finished
: 1;
86 git_vector auth_contexts
;
93 /* Target buffer details from read() */
99 static bool credtype_match(git_http_auth_scheme
*scheme
, void *data
)
101 unsigned int credtype
= *(unsigned int *)data
;
103 return !!(scheme
->credtypes
& credtype
);
106 static bool challenge_match(git_http_auth_scheme
*scheme
, void *data
)
108 const char *scheme_name
= scheme
->name
;
109 const char *challenge
= (const char *)data
;
112 scheme_len
= strlen(scheme_name
);
113 return (strncmp(challenge
, scheme_name
, scheme_len
) == 0 &&
114 (challenge
[scheme_len
] == '\0' || challenge
[scheme_len
] == ' '));
117 static int auth_context_match(
118 git_http_auth_context
**out
,
119 http_subtransport
*t
,
120 bool (*scheme_match
)(git_http_auth_scheme
*scheme
, void *data
),
123 git_http_auth_scheme
*scheme
= NULL
;
124 git_http_auth_context
*context
= NULL
, *c
;
129 for (i
= 0; i
< ARRAY_SIZE(auth_schemes
); i
++) {
130 if (scheme_match(&auth_schemes
[i
], data
)) {
131 scheme
= &auth_schemes
[i
];
139 /* See if authentication has already started for this scheme */
140 git_vector_foreach(&t
->auth_contexts
, i
, c
) {
141 if (c
->type
== scheme
->type
) {
148 if (scheme
->init_context(&context
, &t
->connection_data
) < 0)
152 else if (git_vector_insert(&t
->auth_contexts
, context
) < 0)
161 static int apply_credentials(git_buf
*buf
, http_subtransport
*t
)
163 git_cred
*cred
= t
->cred
;
164 git_http_auth_context
*context
;
166 /* Apply the credentials given to us in the URL */
167 if (!cred
&& t
->connection_data
.user
&& t
->connection_data
.pass
) {
169 git_cred_userpass_plaintext_new(&t
->url_cred
,
170 t
->connection_data
.user
, t
->connection_data
.pass
) < 0)
179 /* Get or create a context for the best scheme for this cred type */
180 if (auth_context_match(&context
, t
, credtype_match
, &cred
->credtype
) < 0)
183 return context
->next_token(buf
, context
, cred
);
186 static int gen_request(
189 size_t content_length
)
191 http_subtransport
*t
= OWNING_SUBTRANSPORT(s
);
192 const char *path
= t
->connection_data
.path
? t
->connection_data
.path
: "/";
194 git_buf_printf(buf
, "%s %s%s HTTP/1.1\r\n", s
->verb
, path
, s
->service_url
);
196 git_buf_puts(buf
, "User-Agent: git/1.0 (libgit2 " LIBGIT2_VERSION
")\r\n");
197 git_buf_printf(buf
, "Host: %s\r\n", t
->connection_data
.host
);
199 if (s
->chunked
|| content_length
> 0) {
200 git_buf_printf(buf
, "Accept: application/x-git-%s-result\r\n", s
->service
);
201 git_buf_printf(buf
, "Content-Type: application/x-git-%s-request\r\n", s
->service
);
204 git_buf_puts(buf
, "Transfer-Encoding: chunked\r\n");
206 git_buf_printf(buf
, "Content-Length: %"PRIuZ
"\r\n", content_length
);
208 git_buf_puts(buf
, "Accept: */*\r\n");
210 /* Apply credentials to the request */
211 if (apply_credentials(buf
, t
) < 0)
214 git_buf_puts(buf
, "\r\n");
216 if (git_buf_oom(buf
))
222 static int parse_authenticate_response(
223 git_vector
*www_authenticate
,
224 http_subtransport
*t
,
227 git_http_auth_context
*context
;
231 git_vector_foreach(www_authenticate
, i
, challenge
) {
232 if (auth_context_match(&context
, t
, challenge_match
, challenge
) < 0)
237 if (context
->set_challenge
&&
238 context
->set_challenge(context
, challenge
) < 0)
241 *allowed_types
|= context
->credtypes
;
247 static int on_header_ready(http_subtransport
*t
)
249 git_buf
*name
= &t
->parse_header_name
;
250 git_buf
*value
= &t
->parse_header_value
;
252 if (!strcasecmp("Content-Type", git_buf_cstr(name
))) {
253 if (!t
->content_type
) {
254 t
->content_type
= git__strdup(git_buf_cstr(value
));
255 GITERR_CHECK_ALLOC(t
->content_type
);
258 else if (!strcasecmp("WWW-Authenticate", git_buf_cstr(name
))) {
259 char *dup
= git__strdup(git_buf_cstr(value
));
260 GITERR_CHECK_ALLOC(dup
);
262 git_vector_insert(&t
->www_authenticate
, dup
);
264 else if (!strcasecmp("Location", git_buf_cstr(name
))) {
266 t
->location
= git__strdup(git_buf_cstr(value
));
267 GITERR_CHECK_ALLOC(t
->location
);
274 static int on_header_field(http_parser
*parser
, const char *str
, size_t len
)
276 parser_context
*ctx
= (parser_context
*) parser
->data
;
277 http_subtransport
*t
= ctx
->t
;
279 /* Both parse_header_name and parse_header_value are populated
280 * and ready for consumption */
281 if (VALUE
== t
->last_cb
)
282 if (on_header_ready(t
) < 0)
283 return t
->parse_error
= PARSE_ERROR_GENERIC
;
285 if (NONE
== t
->last_cb
|| VALUE
== t
->last_cb
)
286 git_buf_clear(&t
->parse_header_name
);
288 if (git_buf_put(&t
->parse_header_name
, str
, len
) < 0)
289 return t
->parse_error
= PARSE_ERROR_GENERIC
;
295 static int on_header_value(http_parser
*parser
, const char *str
, size_t len
)
297 parser_context
*ctx
= (parser_context
*) parser
->data
;
298 http_subtransport
*t
= ctx
->t
;
300 assert(NONE
!= t
->last_cb
);
302 if (FIELD
== t
->last_cb
)
303 git_buf_clear(&t
->parse_header_value
);
305 if (git_buf_put(&t
->parse_header_value
, str
, len
) < 0)
306 return t
->parse_error
= PARSE_ERROR_GENERIC
;
312 static int on_headers_complete(http_parser
*parser
)
314 parser_context
*ctx
= (parser_context
*) parser
->data
;
315 http_subtransport
*t
= ctx
->t
;
316 http_stream
*s
= ctx
->s
;
317 git_buf buf
= GIT_BUF_INIT
;
318 int error
= 0, no_callback
= 0, allowed_auth_types
= 0;
320 /* Both parse_header_name and parse_header_value are populated
321 * and ready for consumption. */
322 if (VALUE
== t
->last_cb
)
323 if (on_header_ready(t
) < 0)
324 return t
->parse_error
= PARSE_ERROR_GENERIC
;
326 /* Capture authentication headers which may be a 401 (authentication
327 * is not complete) or a 200 (simply informing us that auth *is*
330 if (parse_authenticate_response(&t
->www_authenticate
, t
,
331 &allowed_auth_types
) < 0)
332 return t
->parse_error
= PARSE_ERROR_GENERIC
;
334 /* Check for an authentication failure. */
335 if (parser
->status_code
== 401 && get_verb
== s
->verb
) {
336 if (!t
->owner
->cred_acquire_cb
) {
339 if (allowed_auth_types
) {
341 t
->cred
->free(t
->cred
);
345 error
= t
->owner
->cred_acquire_cb(&t
->cred
,
347 t
->connection_data
.user
,
349 t
->owner
->cred_acquire_payload
);
351 if (error
== GIT_PASSTHROUGH
) {
353 } else if (error
< 0) {
354 return PARSE_ERROR_GENERIC
;
358 if (!(t
->cred
->credtype
& allowed_auth_types
)) {
359 giterr_set(GITERR_NET
, "credentials callback returned an invalid cred type");
360 return t
->parse_error
= PARSE_ERROR_GENERIC
;
363 /* Successfully acquired a credential. */
364 t
->parse_error
= PARSE_ERROR_REPLAY
;
371 giterr_set(GITERR_NET
, "authentication required but no callback set");
372 return t
->parse_error
= PARSE_ERROR_GENERIC
;
376 /* Check for a redirect.
377 * Right now we only permit a redirect to the same hostname. */
378 if ((parser
->status_code
== 301 ||
379 parser
->status_code
== 302 ||
380 (parser
->status_code
== 303 && get_verb
== s
->verb
) ||
381 parser
->status_code
== 307) &&
384 if (s
->redirect_count
>= 7) {
385 giterr_set(GITERR_NET
, "Too many redirects");
386 return t
->parse_error
= PARSE_ERROR_GENERIC
;
389 if (gitno_connection_data_from_url(&t
->connection_data
, t
->location
, s
->service_url
) < 0)
390 return t
->parse_error
= PARSE_ERROR_GENERIC
;
392 /* Set the redirect URL on the stream. This is a transfer of
393 * ownership of the memory. */
395 git__free(s
->redirect_url
);
397 s
->redirect_url
= t
->location
;
403 t
->parse_error
= PARSE_ERROR_REPLAY
;
407 /* Check for a 200 HTTP status code. */
408 if (parser
->status_code
!= 200) {
409 giterr_set(GITERR_NET
,
410 "Unexpected HTTP status code: %d",
411 parser
->status_code
);
412 return t
->parse_error
= PARSE_ERROR_GENERIC
;
415 /* The response must contain a Content-Type header. */
416 if (!t
->content_type
) {
417 giterr_set(GITERR_NET
, "No Content-Type header in response");
418 return t
->parse_error
= PARSE_ERROR_GENERIC
;
421 /* The Content-Type header must match our expectation. */
422 if (get_verb
== s
->verb
)
424 "application/x-git-%s-advertisement",
428 "application/x-git-%s-result",
431 if (git_buf_oom(&buf
))
432 return t
->parse_error
= PARSE_ERROR_GENERIC
;
434 if (strcmp(t
->content_type
, git_buf_cstr(&buf
))) {
436 giterr_set(GITERR_NET
,
437 "Invalid Content-Type: %s",
439 return t
->parse_error
= PARSE_ERROR_GENERIC
;
447 static int on_message_complete(http_parser
*parser
)
449 parser_context
*ctx
= (parser_context
*) parser
->data
;
450 http_subtransport
*t
= ctx
->t
;
452 t
->parse_finished
= 1;
457 static int on_body_fill_buffer(http_parser
*parser
, const char *str
, size_t len
)
459 parser_context
*ctx
= (parser_context
*) parser
->data
;
460 http_subtransport
*t
= ctx
->t
;
462 /* If our goal is to replay the request (either an auth failure or
463 * a redirect) then don't bother buffering since we're ignoring the
466 if (t
->parse_error
== PARSE_ERROR_REPLAY
)
469 if (ctx
->buf_size
< len
) {
470 giterr_set(GITERR_NET
, "Can't fit data in the buffer");
471 return t
->parse_error
= PARSE_ERROR_GENERIC
;
474 memcpy(ctx
->buffer
, str
, len
);
475 *(ctx
->bytes_read
) += len
;
477 ctx
->buf_size
-= len
;
482 static void clear_parser_state(http_subtransport
*t
)
484 http_parser_init(&t
->parser
, HTTP_RESPONSE
);
485 gitno_buffer_setup_fromstream(t
->io
,
487 t
->parse_buffer_data
,
488 sizeof(t
->parse_buffer_data
));
492 t
->parse_finished
= 0;
494 git_buf_free(&t
->parse_header_name
);
495 git_buf_init(&t
->parse_header_name
, 0);
497 git_buf_free(&t
->parse_header_value
);
498 git_buf_init(&t
->parse_header_value
, 0);
500 git__free(t
->content_type
);
501 t
->content_type
= NULL
;
503 git__free(t
->location
);
506 git_vector_free_deep(&t
->www_authenticate
);
509 static int write_chunk(git_stream
*io
, const char *buffer
, size_t len
)
511 git_buf buf
= GIT_BUF_INIT
;
514 git_buf_printf(&buf
, "%" PRIxZ
"\r\n", len
);
516 if (git_buf_oom(&buf
))
519 if (git_stream_write(io
, buf
.ptr
, buf
.size
, 0) < 0) {
527 if (len
> 0 && git_stream_write(io
, buffer
, len
, 0) < 0)
531 if (git_stream_write(io
, "\r\n", 2, 0) < 0)
537 static int http_connect(http_subtransport
*t
)
543 http_should_keep_alive(&t
->parser
) &&
548 git_stream_close(t
->io
);
549 git_stream_free(t
->io
);
553 if (t
->connection_data
.use_ssl
) {
554 error
= git_tls_stream_new(&t
->io
, t
->connection_data
.host
, t
->connection_data
.port
);
557 error
= git_curl_stream_new(&t
->io
, t
->connection_data
.host
, t
->connection_data
.port
);
559 error
= git_socket_stream_new(&t
->io
, t
->connection_data
.host
, t
->connection_data
.port
);
566 GITERR_CHECK_VERSION(t
->io
, GIT_STREAM_VERSION
, "git_stream");
568 if (git_stream_supports_proxy(t
->io
) &&
569 !git_remote__get_http_proxy(t
->owner
->owner
, !!t
->connection_data
.use_ssl
, &proxy_url
)) {
570 error
= git_stream_set_proxy(t
->io
, proxy_url
);
571 git__free(proxy_url
);
577 error
= git_stream_connect(t
->io
);
579 #if defined(GIT_OPENSSL) || defined(GIT_SECURE_TRANSPORT) || defined(GIT_CURL)
580 if ((!error
|| error
== GIT_ECERTIFICATE
) && t
->owner
->certificate_check_cb
!= NULL
&&
581 git_stream_is_encrypted(t
->io
)) {
585 if ((error
= git_stream_certificate(&cert
, t
->io
)) < 0)
589 is_valid
= error
!= GIT_ECERTIFICATE
;
590 error
= t
->owner
->certificate_check_cb(cert
, is_valid
, t
->connection_data
.host
, t
->owner
->message_cb_payload
);
594 giterr_set(GITERR_NET
, "user cancelled certificate check");
607 static int http_stream_read(
608 git_smart_subtransport_stream
*stream
,
613 http_stream
*s
= (http_stream
*)stream
;
614 http_subtransport
*t
= OWNING_SUBTRANSPORT(s
);
621 assert(t
->connected
);
623 if (!s
->sent_request
) {
624 git_buf request
= GIT_BUF_INIT
;
626 clear_parser_state(t
);
628 if (gen_request(&request
, s
, 0) < 0)
631 if (git_stream_write(t
->io
, request
.ptr
, request
.size
, 0) < 0) {
632 git_buf_free(&request
);
636 git_buf_free(&request
);
641 if (!s
->received_response
) {
643 assert(s
->verb
== post_verb
);
645 /* Flush, if necessary */
646 if (s
->chunk_buffer_len
> 0 &&
647 write_chunk(t
->io
, s
->chunk_buffer
, s
->chunk_buffer_len
) < 0)
650 s
->chunk_buffer_len
= 0;
652 /* Write the final chunk. */
653 if (git_stream_write(t
->io
, "0\r\n\r\n", 5, 0) < 0)
657 s
->received_response
= 1;
660 while (!*bytes_read
&& !t
->parse_finished
) {
665 * Make the parse_buffer think it's as full of data as
666 * the buffer, so it won't try to recv more data than
667 * we can put into it.
669 * data_offset is the actual data offset from which we
670 * should tell the parser to start reading.
672 if (buf_size
>= t
->parse_buffer
.len
) {
673 t
->parse_buffer
.offset
= 0;
675 t
->parse_buffer
.offset
= t
->parse_buffer
.len
- buf_size
;
678 data_offset
= t
->parse_buffer
.offset
;
680 if (gitno_recv(&t
->parse_buffer
) < 0)
683 /* This call to http_parser_execute will result in invocations of the
684 * on_* family of callbacks. The most interesting of these is
685 * on_body_fill_buffer, which is called when data is ready to be copied
686 * into the target buffer. We need to marshal the buffer, buf_size, and
687 * bytes_read parameters to this callback. */
691 ctx
.buf_size
= buf_size
;
692 ctx
.bytes_read
= bytes_read
;
694 /* Set the context, call the parser, then unset the context. */
695 t
->parser
.data
= &ctx
;
697 bytes_parsed
= http_parser_execute(&t
->parser
,
699 t
->parse_buffer
.data
+ data_offset
,
700 t
->parse_buffer
.offset
- data_offset
);
702 t
->parser
.data
= NULL
;
704 /* If there was a handled authentication failure, then parse_error
705 * will have signaled us that we should replay the request. */
706 if (PARSE_ERROR_REPLAY
== t
->parse_error
) {
709 if ((error
= http_connect(t
)) < 0)
715 if (t
->parse_error
< 0)
718 if (bytes_parsed
!= t
->parse_buffer
.offset
- data_offset
) {
719 giterr_set(GITERR_NET
,
720 "HTTP parser error: %s",
721 http_errno_description((enum http_errno
)t
->parser
.http_errno
));
729 static int http_stream_write_chunked(
730 git_smart_subtransport_stream
*stream
,
734 http_stream
*s
= (http_stream
*)stream
;
735 http_subtransport
*t
= OWNING_SUBTRANSPORT(s
);
737 assert(t
->connected
);
739 /* Send the request, if necessary */
740 if (!s
->sent_request
) {
741 git_buf request
= GIT_BUF_INIT
;
743 clear_parser_state(t
);
745 if (gen_request(&request
, s
, 0) < 0)
748 if (git_stream_write(t
->io
, request
.ptr
, request
.size
, 0) < 0) {
749 git_buf_free(&request
);
753 git_buf_free(&request
);
758 if (len
> CHUNK_SIZE
) {
759 /* Flush, if necessary */
760 if (s
->chunk_buffer_len
> 0) {
761 if (write_chunk(t
->io
, s
->chunk_buffer
, s
->chunk_buffer_len
) < 0)
764 s
->chunk_buffer_len
= 0;
767 /* Write chunk directly */
768 if (write_chunk(t
->io
, buffer
, len
) < 0)
772 /* Append as much to the buffer as we can */
773 int count
= min(CHUNK_SIZE
- s
->chunk_buffer_len
, len
);
775 if (!s
->chunk_buffer
)
776 s
->chunk_buffer
= git__malloc(CHUNK_SIZE
);
778 memcpy(s
->chunk_buffer
+ s
->chunk_buffer_len
, buffer
, count
);
779 s
->chunk_buffer_len
+= count
;
783 /* Is the buffer full? If so, then flush */
784 if (CHUNK_SIZE
== s
->chunk_buffer_len
) {
785 if (write_chunk(t
->io
, s
->chunk_buffer
, s
->chunk_buffer_len
) < 0)
788 s
->chunk_buffer_len
= 0;
791 memcpy(s
->chunk_buffer
, buffer
, len
);
792 s
->chunk_buffer_len
= len
;
800 static int http_stream_write_single(
801 git_smart_subtransport_stream
*stream
,
805 http_stream
*s
= (http_stream
*)stream
;
806 http_subtransport
*t
= OWNING_SUBTRANSPORT(s
);
807 git_buf request
= GIT_BUF_INIT
;
809 assert(t
->connected
);
811 if (s
->sent_request
) {
812 giterr_set(GITERR_NET
, "Subtransport configured for only one write");
816 clear_parser_state(t
);
818 if (gen_request(&request
, s
, len
) < 0)
821 if (git_stream_write(t
->io
, request
.ptr
, request
.size
, 0) < 0)
824 if (len
&& git_stream_write(t
->io
, buffer
, len
, 0) < 0)
827 git_buf_free(&request
);
833 git_buf_free(&request
);
837 static void http_stream_free(git_smart_subtransport_stream
*stream
)
839 http_stream
*s
= (http_stream
*)stream
;
842 git__free(s
->chunk_buffer
);
845 git__free(s
->redirect_url
);
850 static int http_stream_alloc(http_subtransport
*t
,
851 git_smart_subtransport_stream
**stream
)
858 s
= git__calloc(sizeof(http_stream
), 1);
859 GITERR_CHECK_ALLOC(s
);
861 s
->parent
.subtransport
= &t
->parent
;
862 s
->parent
.read
= http_stream_read
;
863 s
->parent
.write
= http_stream_write_single
;
864 s
->parent
.free
= http_stream_free
;
866 *stream
= (git_smart_subtransport_stream
*)s
;
870 static int http_uploadpack_ls(
871 http_subtransport
*t
,
872 git_smart_subtransport_stream
**stream
)
876 if (http_stream_alloc(t
, stream
) < 0)
879 s
= (http_stream
*)*stream
;
881 s
->service
= upload_pack_service
;
882 s
->service_url
= upload_pack_ls_service_url
;
888 static int http_uploadpack(
889 http_subtransport
*t
,
890 git_smart_subtransport_stream
**stream
)
894 if (http_stream_alloc(t
, stream
) < 0)
897 s
= (http_stream
*)*stream
;
899 s
->service
= upload_pack_service
;
900 s
->service_url
= upload_pack_service_url
;
906 static int http_receivepack_ls(
907 http_subtransport
*t
,
908 git_smart_subtransport_stream
**stream
)
912 if (http_stream_alloc(t
, stream
) < 0)
915 s
= (http_stream
*)*stream
;
917 s
->service
= receive_pack_service
;
918 s
->service_url
= receive_pack_ls_service_url
;
924 static int http_receivepack(
925 http_subtransport
*t
,
926 git_smart_subtransport_stream
**stream
)
930 if (http_stream_alloc(t
, stream
) < 0)
933 s
= (http_stream
*)*stream
;
935 /* Use Transfer-Encoding: chunked for this request */
937 s
->parent
.write
= http_stream_write_chunked
;
939 s
->service
= receive_pack_service
;
940 s
->service_url
= receive_pack_service_url
;
946 static int http_action(
947 git_smart_subtransport_stream
**stream
,
948 git_smart_subtransport
*subtransport
,
950 git_smart_service_t action
)
952 http_subtransport
*t
= (http_subtransport
*)subtransport
;
958 if ((!t
->connection_data
.host
|| !t
->connection_data
.port
|| !t
->connection_data
.path
) &&
959 (ret
= gitno_connection_data_from_url(&t
->connection_data
, url
, NULL
)) < 0)
962 if ((ret
= http_connect(t
)) < 0)
966 case GIT_SERVICE_UPLOADPACK_LS
:
967 return http_uploadpack_ls(t
, stream
);
969 case GIT_SERVICE_UPLOADPACK
:
970 return http_uploadpack(t
, stream
);
972 case GIT_SERVICE_RECEIVEPACK_LS
:
973 return http_receivepack_ls(t
, stream
);
975 case GIT_SERVICE_RECEIVEPACK
:
976 return http_receivepack(t
, stream
);
983 static int http_close(git_smart_subtransport
*subtransport
)
985 http_subtransport
*t
= (http_subtransport
*) subtransport
;
986 git_http_auth_context
*context
;
989 clear_parser_state(t
);
992 git_stream_close(t
->io
);
993 git_stream_free(t
->io
);
998 t
->cred
->free(t
->cred
);
1003 t
->url_cred
->free(t
->url_cred
);
1007 git_vector_foreach(&t
->auth_contexts
, i
, context
) {
1009 context
->free(context
);
1012 git_vector_clear(&t
->auth_contexts
);
1014 gitno_connection_data_free_ptrs(&t
->connection_data
);
1015 memset(&t
->connection_data
, 0x0, sizeof(gitno_connection_data
));
1020 static void http_free(git_smart_subtransport
*subtransport
)
1022 http_subtransport
*t
= (http_subtransport
*) subtransport
;
1024 http_close(subtransport
);
1026 git_vector_free(&t
->auth_contexts
);
1030 int git_smart_subtransport_http(git_smart_subtransport
**out
, git_transport
*owner
, void *param
)
1032 http_subtransport
*t
;
1039 t
= git__calloc(sizeof(http_subtransport
), 1);
1040 GITERR_CHECK_ALLOC(t
);
1042 t
->owner
= (transport_smart
*)owner
;
1043 t
->parent
.action
= http_action
;
1044 t
->parent
.close
= http_close
;
1045 t
->parent
.free
= http_free
;
1047 t
->settings
.on_header_field
= on_header_field
;
1048 t
->settings
.on_header_value
= on_header_value
;
1049 t
->settings
.on_headers_complete
= on_headers_complete
;
1050 t
->settings
.on_body
= on_body_fill_buffer
;
1051 t
->settings
.on_message_complete
= on_message_complete
;
1053 *out
= (git_smart_subtransport
*) t
;
1057 #endif /* !GIT_WINHTTP */