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
39 /** Look at the user field */
40 #define PARSE_ERROR_EXT -3
42 #define CHUNK_SIZE 4096
51 git_smart_subtransport_stream parent
;
53 const char *service_url
;
57 unsigned chunk_buffer_len
;
58 unsigned sent_request
: 1,
59 received_response
: 1,
65 git_smart_subtransport parent
;
66 transport_smart
*owner
;
68 gitno_connection_data connection_data
;
71 /* Parser structures */
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
];
80 git_vector www_authenticate
;
84 unsigned parse_finished
: 1;
89 git_vector auth_contexts
;
96 /* Target buffer details from read() */
102 static bool credtype_match(git_http_auth_scheme
*scheme
, void *data
)
104 unsigned int credtype
= *(unsigned int *)data
;
106 return !!(scheme
->credtypes
& credtype
);
109 static bool challenge_match(git_http_auth_scheme
*scheme
, void *data
)
111 const char *scheme_name
= scheme
->name
;
112 const char *challenge
= (const char *)data
;
115 scheme_len
= strlen(scheme_name
);
116 return (strncmp(challenge
, scheme_name
, scheme_len
) == 0 &&
117 (challenge
[scheme_len
] == '\0' || challenge
[scheme_len
] == ' '));
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
),
126 git_http_auth_scheme
*scheme
= NULL
;
127 git_http_auth_context
*context
= NULL
, *c
;
132 for (i
= 0; i
< ARRAY_SIZE(auth_schemes
); i
++) {
133 if (scheme_match(&auth_schemes
[i
], data
)) {
134 scheme
= &auth_schemes
[i
];
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
) {
151 if (scheme
->init_context(&context
, &t
->connection_data
) < 0)
155 else if (git_vector_insert(&t
->auth_contexts
, context
) < 0)
164 static int apply_credentials(git_buf
*buf
, http_subtransport
*t
)
166 git_cred
*cred
= t
->cred
;
167 git_http_auth_context
*context
;
169 /* Apply the credentials given to us in the URL */
170 if (!cred
&& t
->connection_data
.user
&& t
->connection_data
.pass
) {
172 git_cred_userpass_plaintext_new(&t
->url_cred
,
173 t
->connection_data
.user
, t
->connection_data
.pass
) < 0)
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)
186 return context
->next_token(buf
, context
, cred
);
189 static int gen_request(
192 size_t content_length
)
194 http_subtransport
*t
= OWNING_SUBTRANSPORT(s
);
195 const char *path
= t
->connection_data
.path
? t
->connection_data
.path
: "/";
197 git_buf_printf(buf
, "%s %s%s HTTP/1.1\r\n", s
->verb
, path
, s
->service_url
);
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
);
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
);
207 git_buf_puts(buf
, "Transfer-Encoding: chunked\r\n");
209 git_buf_printf(buf
, "Content-Length: %"PRIuZ
"\r\n", content_length
);
211 git_buf_puts(buf
, "Accept: */*\r\n");
213 /* Apply credentials to the request */
214 if (apply_credentials(buf
, t
) < 0)
217 git_buf_puts(buf
, "\r\n");
219 if (git_buf_oom(buf
))
225 static int parse_authenticate_response(
226 git_vector
*www_authenticate
,
227 http_subtransport
*t
,
230 git_http_auth_context
*context
;
234 git_vector_foreach(www_authenticate
, i
, challenge
) {
235 if (auth_context_match(&context
, t
, challenge_match
, challenge
) < 0)
240 if (context
->set_challenge
&&
241 context
->set_challenge(context
, challenge
) < 0)
244 *allowed_types
|= context
->credtypes
;
250 static int on_header_ready(http_subtransport
*t
)
252 git_buf
*name
= &t
->parse_header_name
;
253 git_buf
*value
= &t
->parse_header_value
;
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
);
261 else if (!strcasecmp("WWW-Authenticate", git_buf_cstr(name
))) {
262 char *dup
= git__strdup(git_buf_cstr(value
));
263 GITERR_CHECK_ALLOC(dup
);
265 git_vector_insert(&t
->www_authenticate
, dup
);
267 else if (!strcasecmp("Location", git_buf_cstr(name
))) {
269 t
->location
= git__strdup(git_buf_cstr(value
));
270 GITERR_CHECK_ALLOC(t
->location
);
277 static int on_header_field(http_parser
*parser
, const char *str
, size_t len
)
279 parser_context
*ctx
= (parser_context
*) parser
->data
;
280 http_subtransport
*t
= ctx
->t
;
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
;
288 if (NONE
== t
->last_cb
|| VALUE
== t
->last_cb
)
289 git_buf_clear(&t
->parse_header_name
);
291 if (git_buf_put(&t
->parse_header_name
, str
, len
) < 0)
292 return t
->parse_error
= PARSE_ERROR_GENERIC
;
298 static int on_header_value(http_parser
*parser
, const char *str
, size_t len
)
300 parser_context
*ctx
= (parser_context
*) parser
->data
;
301 http_subtransport
*t
= ctx
->t
;
303 assert(NONE
!= t
->last_cb
);
305 if (FIELD
== t
->last_cb
)
306 git_buf_clear(&t
->parse_header_value
);
308 if (git_buf_put(&t
->parse_header_value
, str
, len
) < 0)
309 return t
->parse_error
= PARSE_ERROR_GENERIC
;
315 static int on_headers_complete(http_parser
*parser
)
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;
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
;
329 /* Capture authentication headers which may be a 401 (authentication
330 * is not complete) or a 200 (simply informing us that auth *is*
333 if (parse_authenticate_response(&t
->www_authenticate
, t
,
334 &allowed_auth_types
) < 0)
335 return t
->parse_error
= PARSE_ERROR_GENERIC
;
337 /* Check for an authentication failure. */
338 if (parser
->status_code
== 401 && get_verb
== s
->verb
) {
339 if (!t
->owner
->cred_acquire_cb
) {
342 if (allowed_auth_types
) {
344 t
->cred
->free(t
->cred
);
348 error
= t
->owner
->cred_acquire_cb(&t
->cred
,
350 t
->connection_data
.user
,
352 t
->owner
->cred_acquire_payload
);
354 if (error
== GIT_PASSTHROUGH
) {
356 } else if (error
< 0) {
358 return t
->parse_error
= PARSE_ERROR_EXT
;
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
;
367 /* Successfully acquired a credential. */
368 t
->parse_error
= PARSE_ERROR_REPLAY
;
375 giterr_set(GITERR_NET
, "authentication required but no callback set");
376 return t
->parse_error
= PARSE_ERROR_GENERIC
;
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) &&
388 if (s
->redirect_count
>= 7) {
389 giterr_set(GITERR_NET
, "Too many redirects");
390 return t
->parse_error
= PARSE_ERROR_GENERIC
;
393 if (gitno_connection_data_from_url(&t
->connection_data
, t
->location
, s
->service_url
) < 0)
394 return t
->parse_error
= PARSE_ERROR_GENERIC
;
396 /* Set the redirect URL on the stream. This is a transfer of
397 * ownership of the memory. */
399 git__free(s
->redirect_url
);
401 s
->redirect_url
= t
->location
;
407 t
->parse_error
= PARSE_ERROR_REPLAY
;
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
;
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
;
425 /* The Content-Type header must match our expectation. */
426 if (get_verb
== s
->verb
)
428 "application/x-git-%s-advertisement",
432 "application/x-git-%s-result",
435 if (git_buf_oom(&buf
))
436 return t
->parse_error
= PARSE_ERROR_GENERIC
;
438 if (strcmp(t
->content_type
, git_buf_cstr(&buf
))) {
440 giterr_set(GITERR_NET
,
441 "Invalid Content-Type: %s",
443 return t
->parse_error
= PARSE_ERROR_GENERIC
;
451 static int on_message_complete(http_parser
*parser
)
453 parser_context
*ctx
= (parser_context
*) parser
->data
;
454 http_subtransport
*t
= ctx
->t
;
456 t
->parse_finished
= 1;
461 static int on_body_fill_buffer(http_parser
*parser
, const char *str
, size_t len
)
463 parser_context
*ctx
= (parser_context
*) parser
->data
;
464 http_subtransport
*t
= ctx
->t
;
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
470 if (t
->parse_error
== PARSE_ERROR_REPLAY
)
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
;
478 memcpy(ctx
->buffer
, str
, len
);
479 *(ctx
->bytes_read
) += len
;
481 ctx
->buf_size
-= len
;
486 static void clear_parser_state(http_subtransport
*t
)
488 http_parser_init(&t
->parser
, HTTP_RESPONSE
);
489 gitno_buffer_setup_fromstream(t
->io
,
491 t
->parse_buffer_data
,
492 sizeof(t
->parse_buffer_data
));
496 t
->parse_finished
= 0;
498 git_buf_free(&t
->parse_header_name
);
499 git_buf_init(&t
->parse_header_name
, 0);
501 git_buf_free(&t
->parse_header_value
);
502 git_buf_init(&t
->parse_header_value
, 0);
504 git__free(t
->content_type
);
505 t
->content_type
= NULL
;
507 git__free(t
->location
);
510 git_vector_free_deep(&t
->www_authenticate
);
513 static int write_chunk(git_stream
*io
, const char *buffer
, size_t len
)
515 git_buf buf
= GIT_BUF_INIT
;
518 git_buf_printf(&buf
, "%" PRIxZ
"\r\n", len
);
520 if (git_buf_oom(&buf
))
523 if (git_stream_write(io
, buf
.ptr
, buf
.size
, 0) < 0) {
531 if (len
> 0 && git_stream_write(io
, buffer
, len
, 0) < 0)
535 if (git_stream_write(io
, "\r\n", 2, 0) < 0)
541 static int http_connect(http_subtransport
*t
)
547 http_should_keep_alive(&t
->parser
) &&
552 git_stream_close(t
->io
);
553 git_stream_free(t
->io
);
557 if (t
->connection_data
.use_ssl
) {
558 error
= git_tls_stream_new(&t
->io
, t
->connection_data
.host
, t
->connection_data
.port
);
561 error
= git_curl_stream_new(&t
->io
, t
->connection_data
.host
, t
->connection_data
.port
);
563 error
= git_socket_stream_new(&t
->io
, t
->connection_data
.host
, t
->connection_data
.port
);
570 GITERR_CHECK_VERSION(t
->io
, GIT_STREAM_VERSION
, "git_stream");
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
);
581 error
= git_stream_connect(t
->io
);
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
)) {
589 if ((error
= git_stream_certificate(&cert
, t
->io
)) < 0)
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
);
598 giterr_set(GITERR_NET
, "user cancelled certificate check");
611 static int http_stream_read(
612 git_smart_subtransport_stream
*stream
,
617 http_stream
*s
= (http_stream
*)stream
;
618 http_subtransport
*t
= OWNING_SUBTRANSPORT(s
);
625 assert(t
->connected
);
627 if (!s
->sent_request
) {
628 git_buf request
= GIT_BUF_INIT
;
630 clear_parser_state(t
);
632 if (gen_request(&request
, s
, 0) < 0)
635 if (git_stream_write(t
->io
, request
.ptr
, request
.size
, 0) < 0) {
636 git_buf_free(&request
);
640 git_buf_free(&request
);
645 if (!s
->received_response
) {
647 assert(s
->verb
== post_verb
);
649 /* Flush, if necessary */
650 if (s
->chunk_buffer_len
> 0 &&
651 write_chunk(t
->io
, s
->chunk_buffer
, s
->chunk_buffer_len
) < 0)
654 s
->chunk_buffer_len
= 0;
656 /* Write the final chunk. */
657 if (git_stream_write(t
->io
, "0\r\n\r\n", 5, 0) < 0)
661 s
->received_response
= 1;
664 while (!*bytes_read
&& !t
->parse_finished
) {
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.
673 * data_offset is the actual data offset from which we
674 * should tell the parser to start reading.
676 if (buf_size
>= t
->parse_buffer
.len
) {
677 t
->parse_buffer
.offset
= 0;
679 t
->parse_buffer
.offset
= t
->parse_buffer
.len
- buf_size
;
682 data_offset
= t
->parse_buffer
.offset
;
684 if (gitno_recv(&t
->parse_buffer
) < 0)
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. */
695 ctx
.buf_size
= buf_size
;
696 ctx
.bytes_read
= bytes_read
;
698 /* Set the context, call the parser, then unset the context. */
699 t
->parser
.data
= &ctx
;
701 bytes_parsed
= http_parser_execute(&t
->parser
,
703 t
->parse_buffer
.data
+ data_offset
,
704 t
->parse_buffer
.offset
- data_offset
);
706 t
->parser
.data
= NULL
;
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
) {
713 if ((error
= http_connect(t
)) < 0)
719 if (t
->parse_error
== PARSE_ERROR_EXT
) {
723 if (t
->parse_error
< 0)
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
));
737 static int http_stream_write_chunked(
738 git_smart_subtransport_stream
*stream
,
742 http_stream
*s
= (http_stream
*)stream
;
743 http_subtransport
*t
= OWNING_SUBTRANSPORT(s
);
745 assert(t
->connected
);
747 /* Send the request, if necessary */
748 if (!s
->sent_request
) {
749 git_buf request
= GIT_BUF_INIT
;
751 clear_parser_state(t
);
753 if (gen_request(&request
, s
, 0) < 0)
756 if (git_stream_write(t
->io
, request
.ptr
, request
.size
, 0) < 0) {
757 git_buf_free(&request
);
761 git_buf_free(&request
);
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)
772 s
->chunk_buffer_len
= 0;
775 /* Write chunk directly */
776 if (write_chunk(t
->io
, buffer
, len
) < 0)
780 /* Append as much to the buffer as we can */
781 int count
= min(CHUNK_SIZE
- s
->chunk_buffer_len
, len
);
783 if (!s
->chunk_buffer
)
784 s
->chunk_buffer
= git__malloc(CHUNK_SIZE
);
786 memcpy(s
->chunk_buffer
+ s
->chunk_buffer_len
, buffer
, count
);
787 s
->chunk_buffer_len
+= count
;
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)
796 s
->chunk_buffer_len
= 0;
799 memcpy(s
->chunk_buffer
, buffer
, len
);
800 s
->chunk_buffer_len
= len
;
808 static int http_stream_write_single(
809 git_smart_subtransport_stream
*stream
,
813 http_stream
*s
= (http_stream
*)stream
;
814 http_subtransport
*t
= OWNING_SUBTRANSPORT(s
);
815 git_buf request
= GIT_BUF_INIT
;
817 assert(t
->connected
);
819 if (s
->sent_request
) {
820 giterr_set(GITERR_NET
, "Subtransport configured for only one write");
824 clear_parser_state(t
);
826 if (gen_request(&request
, s
, len
) < 0)
829 if (git_stream_write(t
->io
, request
.ptr
, request
.size
, 0) < 0)
832 if (len
&& git_stream_write(t
->io
, buffer
, len
, 0) < 0)
835 git_buf_free(&request
);
841 git_buf_free(&request
);
845 static void http_stream_free(git_smart_subtransport_stream
*stream
)
847 http_stream
*s
= (http_stream
*)stream
;
850 git__free(s
->chunk_buffer
);
853 git__free(s
->redirect_url
);
858 static int http_stream_alloc(http_subtransport
*t
,
859 git_smart_subtransport_stream
**stream
)
866 s
= git__calloc(sizeof(http_stream
), 1);
867 GITERR_CHECK_ALLOC(s
);
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
;
874 *stream
= (git_smart_subtransport_stream
*)s
;
878 static int http_uploadpack_ls(
879 http_subtransport
*t
,
880 git_smart_subtransport_stream
**stream
)
884 if (http_stream_alloc(t
, stream
) < 0)
887 s
= (http_stream
*)*stream
;
889 s
->service
= upload_pack_service
;
890 s
->service_url
= upload_pack_ls_service_url
;
896 static int http_uploadpack(
897 http_subtransport
*t
,
898 git_smart_subtransport_stream
**stream
)
902 if (http_stream_alloc(t
, stream
) < 0)
905 s
= (http_stream
*)*stream
;
907 s
->service
= upload_pack_service
;
908 s
->service_url
= upload_pack_service_url
;
914 static int http_receivepack_ls(
915 http_subtransport
*t
,
916 git_smart_subtransport_stream
**stream
)
920 if (http_stream_alloc(t
, stream
) < 0)
923 s
= (http_stream
*)*stream
;
925 s
->service
= receive_pack_service
;
926 s
->service_url
= receive_pack_ls_service_url
;
932 static int http_receivepack(
933 http_subtransport
*t
,
934 git_smart_subtransport_stream
**stream
)
938 if (http_stream_alloc(t
, stream
) < 0)
941 s
= (http_stream
*)*stream
;
943 /* Use Transfer-Encoding: chunked for this request */
945 s
->parent
.write
= http_stream_write_chunked
;
947 s
->service
= receive_pack_service
;
948 s
->service_url
= receive_pack_service_url
;
954 static int http_action(
955 git_smart_subtransport_stream
**stream
,
956 git_smart_subtransport
*subtransport
,
958 git_smart_service_t action
)
960 http_subtransport
*t
= (http_subtransport
*)subtransport
;
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)
970 if ((ret
= http_connect(t
)) < 0)
974 case GIT_SERVICE_UPLOADPACK_LS
:
975 return http_uploadpack_ls(t
, stream
);
977 case GIT_SERVICE_UPLOADPACK
:
978 return http_uploadpack(t
, stream
);
980 case GIT_SERVICE_RECEIVEPACK_LS
:
981 return http_receivepack_ls(t
, stream
);
983 case GIT_SERVICE_RECEIVEPACK
:
984 return http_receivepack(t
, stream
);
991 static int http_close(git_smart_subtransport
*subtransport
)
993 http_subtransport
*t
= (http_subtransport
*) subtransport
;
994 git_http_auth_context
*context
;
997 clear_parser_state(t
);
1000 git_stream_close(t
->io
);
1001 git_stream_free(t
->io
);
1006 t
->cred
->free(t
->cred
);
1011 t
->url_cred
->free(t
->url_cred
);
1015 git_vector_foreach(&t
->auth_contexts
, i
, context
) {
1017 context
->free(context
);
1020 git_vector_clear(&t
->auth_contexts
);
1022 gitno_connection_data_free_ptrs(&t
->connection_data
);
1023 memset(&t
->connection_data
, 0x0, sizeof(gitno_connection_data
));
1028 static void http_free(git_smart_subtransport
*subtransport
)
1030 http_subtransport
*t
= (http_subtransport
*) subtransport
;
1032 http_close(subtransport
);
1034 git_vector_free(&t
->auth_contexts
);
1038 int git_smart_subtransport_http(git_smart_subtransport
**out
, git_transport
*owner
, void *param
)
1040 http_subtransport
*t
;
1047 t
= git__calloc(sizeof(http_subtransport
), 1);
1048 GITERR_CHECK_ALLOC(t
);
1050 t
->owner
= (transport_smart
*)owner
;
1051 t
->parent
.action
= http_action
;
1052 t
->parent
.close
= http_close
;
1053 t
->parent
.free
= http_free
;
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
;
1061 *out
= (git_smart_subtransport
*) t
;
1065 #endif /* !GIT_WINHTTP */