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.
12 #include "streams/socket.h"
13 #include "git2/sys/transport.h"
15 #define OWNING_SUBTRANSPORT(s) ((git_subtransport *)(s)->parent.subtransport)
17 static const char prefix_git
[] = "git://";
18 static const char cmd_uploadpack
[] = "git-upload-pack";
19 static const char cmd_receivepack
[] = "git-receive-pack";
22 git_smart_subtransport_stream parent
;
26 unsigned sent_command
: 1;
30 git_smart_subtransport parent
;
32 git_proto_stream
*current_stream
;
36 * Create a git protocol request.
38 * For example: 0035git-upload-pack /libgit2/libgit2\0host=github.com\0
40 static int gen_proto(git_str
*request
, const char *cmd
, const char *url
)
43 char host
[] = "host=";
46 delim
= strchr(url
, '/');
48 git_error_set(GIT_ERROR_NET
, "malformed URL");
56 delim
= strchr(url
, ':');
58 delim
= strchr(url
, '/');
60 len
= 4 + strlen(cmd
) + 1 + strlen(repo
) + 1 + strlen(host
) + (delim
- url
) + 1;
62 git_str_grow(request
, len
);
63 git_str_printf(request
, "%04x%s %s%c%s",
64 (unsigned int)(len
& 0x0FFFF), cmd
, repo
, 0, host
);
65 git_str_put(request
, url
, delim
- url
);
66 git_str_putc(request
, '\0');
68 if (git_str_oom(request
))
74 static int send_command(git_proto_stream
*s
)
76 git_str request
= GIT_STR_INIT
;
79 if ((error
= gen_proto(&request
, s
->cmd
, s
->url
)) < 0)
82 if ((error
= git_stream__write_full(s
->io
, request
.ptr
, request
.size
, 0)) < 0)
88 git_str_dispose(&request
);
92 static int git_proto_stream_read(
93 git_smart_subtransport_stream
*stream
,
99 git_proto_stream
*s
= (git_proto_stream
*)stream
;
104 if (!s
->sent_command
&& (error
= send_command(s
)) < 0)
107 gitno_buffer_setup_fromstream(s
->io
, &buf
, buffer
, buf_size
);
109 if ((error
= gitno_recv(&buf
)) < 0)
112 *bytes_read
= buf
.offset
;
117 static int git_proto_stream_write(
118 git_smart_subtransport_stream
*stream
,
122 git_proto_stream
*s
= (git_proto_stream
*)stream
;
125 if (!s
->sent_command
&& (error
= send_command(s
)) < 0)
128 return git_stream__write_full(s
->io
, buffer
, len
, 0);
131 static void git_proto_stream_free(git_smart_subtransport_stream
*stream
)
139 s
= (git_proto_stream
*)stream
;
140 t
= OWNING_SUBTRANSPORT(s
);
142 t
->current_stream
= NULL
;
144 git_stream_close(s
->io
);
145 git_stream_free(s
->io
);
150 static int git_proto_stream_alloc(
156 git_smart_subtransport_stream
**stream
)
163 s
= git__calloc(1, sizeof(git_proto_stream
));
164 GIT_ERROR_CHECK_ALLOC(s
);
166 s
->parent
.subtransport
= &t
->parent
;
167 s
->parent
.read
= git_proto_stream_read
;
168 s
->parent
.write
= git_proto_stream_write
;
169 s
->parent
.free
= git_proto_stream_free
;
172 s
->url
= git__strdup(url
);
179 if ((git_socket_stream_new(&s
->io
, host
, port
)) < 0)
182 GIT_ERROR_CHECK_VERSION(s
->io
, GIT_STREAM_VERSION
, "git_stream");
184 *stream
= &s
->parent
;
188 static int _git_uploadpack_ls(
191 git_smart_subtransport_stream
**stream
)
193 git_net_url urldata
= GIT_NET_URL_INIT
;
194 const char *stream_url
= url
;
195 const char *host
, *port
;
201 if (!git__prefixcmp(url
, prefix_git
))
202 stream_url
+= strlen(prefix_git
);
204 if ((error
= git_net_url_parse(&urldata
, url
)) < 0)
208 port
= urldata
.port
? urldata
.port
: GIT_DEFAULT_PORT
;
210 error
= git_proto_stream_alloc(t
, stream_url
, cmd_uploadpack
, host
, port
, stream
);
212 git_net_url_dispose(&urldata
);
215 git_proto_stream_free(*stream
);
219 s
= (git_proto_stream
*) *stream
;
220 if ((error
= git_stream_connect(s
->io
)) < 0) {
221 git_proto_stream_free(*stream
);
225 t
->current_stream
= s
;
230 static int _git_uploadpack(
233 git_smart_subtransport_stream
**stream
)
237 if (t
->current_stream
) {
238 *stream
= &t
->current_stream
->parent
;
242 git_error_set(GIT_ERROR_NET
, "must call UPLOADPACK_LS before UPLOADPACK");
246 static int _git_receivepack_ls(
249 git_smart_subtransport_stream
**stream
)
251 git_net_url urldata
= GIT_NET_URL_INIT
;
252 const char *stream_url
= url
;
257 if (!git__prefixcmp(url
, prefix_git
))
258 stream_url
+= strlen(prefix_git
);
260 if ((error
= git_net_url_parse(&urldata
, url
)) < 0)
263 error
= git_proto_stream_alloc(t
, stream_url
, cmd_receivepack
, urldata
.host
, urldata
.port
, stream
);
265 git_net_url_dispose(&urldata
);
268 git_proto_stream_free(*stream
);
272 s
= (git_proto_stream
*) *stream
;
274 if ((error
= git_stream_connect(s
->io
)) < 0)
277 t
->current_stream
= s
;
282 static int _git_receivepack(
285 git_smart_subtransport_stream
**stream
)
289 if (t
->current_stream
) {
290 *stream
= &t
->current_stream
->parent
;
294 git_error_set(GIT_ERROR_NET
, "must call RECEIVEPACK_LS before RECEIVEPACK");
298 static int _git_action(
299 git_smart_subtransport_stream
**stream
,
300 git_smart_subtransport
*subtransport
,
302 git_smart_service_t action
)
304 git_subtransport
*t
= (git_subtransport
*) subtransport
;
307 case GIT_SERVICE_UPLOADPACK_LS
:
308 return _git_uploadpack_ls(t
, url
, stream
);
310 case GIT_SERVICE_UPLOADPACK
:
311 return _git_uploadpack(t
, url
, stream
);
313 case GIT_SERVICE_RECEIVEPACK_LS
:
314 return _git_receivepack_ls(t
, url
, stream
);
316 case GIT_SERVICE_RECEIVEPACK
:
317 return _git_receivepack(t
, url
, stream
);
324 static int _git_close(git_smart_subtransport
*subtransport
)
326 git_subtransport
*t
= (git_subtransport
*) subtransport
;
328 GIT_ASSERT(!t
->current_stream
);
335 static void _git_free(git_smart_subtransport
*subtransport
)
337 git_subtransport
*t
= (git_subtransport
*) subtransport
;
342 int git_smart_subtransport_git(git_smart_subtransport
**out
, git_transport
*owner
, void *param
)
351 t
= git__calloc(1, sizeof(git_subtransport
));
352 GIT_ERROR_CHECK_ALLOC(t
);
355 t
->parent
.action
= _git_action
;
356 t
->parent
.close
= _git_close
;
357 t
->parent
.free
= _git_free
;
359 *out
= (git_smart_subtransport
*) t
;