* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
-
#include <stdlib.h>
#include "git2.h"
#include "http_parser.h"
#include "filebuf.h"
#include "repository.h"
#include "protocol.h"
+#if GIT_WINHTTP
+# include <winhttp.h>
+# pragma comment(lib, "winhttp.lib")
+#endif
+
+#define WIDEN2(s) L ## s
+#define WIDEN(s) WIDEN2(s)
enum last_cb {
NONE,
#ifdef GIT_WIN32
WSADATA wsd;
#endif
+#ifdef GIT_WINHTTP
+ HINTERNET session;
+ HINTERNET connection;
+ HINTERNET request;
+#endif
} transport_http;
static int gen_request(git_buf *buf, const char *path, const char *host, const char *op,
return 0;
}
-static int do_connect(transport_http *t, const char *host, const char *port)
+static int send_request(transport_http *t, const char *service, void *data, ssize_t content_length, int ls)
{
+#ifndef GIT_WINHTTP
+ git_buf request = GIT_BUF_INIT;
+ const char *verb;
+
+ verb = ls ? "GET" : "POST";
+ /* Generate and send the HTTP request */
+ if (gen_request(&request, t->path, t->host, verb, service, content_length, ls) < 0) {
+ giterr_set(GITERR_NET, "Failed to generate request");
+ return -1;
+ }
+
+
+ if (gitno_send((git_transport *) t, request.ptr, request.size, 0) < 0) {
+ git_buf_free(&request);
+ return -1;
+ }
+
+ if (content_length) {
+ if (gitno_send((git_transport *) t, data, content_length, 0) < 0)
+ return -1;
+ }
+
+ return 0;
+#else
+ wchar_t *url, *verb, *ct;
+ git_buf buf = GIT_BUF_INIT;
+ BOOL ret;
+ DWORD flags;
+ void *buffer;
+ wchar_t *types[] = {
+ L"*/*",
+ NULL,
+ };
+
+ verb = ls ? L"GET" : L"POST";
+ buffer = data ? data : WINHTTP_NO_REQUEST_DATA;
+ flags = t->parent.use_ssl ? WINHTTP_FLAG_SECURE : 0;
+
+ if (ls)
+ git_buf_printf(&buf, "%s/info/refs?service=git-%s", t->path, service);
+ else
+ git_buf_printf(&buf, "%s/git-%s", t->path, service);
+
+ if (git_buf_oom(&buf))
+ return -1;
+
+ url = gitwin_to_utf16(git_buf_cstr(&buf));
+ if (!url)
+ goto on_error;
+
+ t->request = WinHttpOpenRequest(t->connection, verb, url, NULL, WINHTTP_NO_REFERER, types, flags);
+ git__free(url);
+ if (t->request == NULL) {
+ git_buf_free(&buf);
+ giterr_set(GITERR_OS, "Failed to open request");
+ return -1;
+ }
+
+ git_buf_clear(&buf);
+ if (git_buf_printf(&buf, "Content-Type: application/x-git-%s-request", service) < 0)
+ goto on_error;
+ ct = gitwin_to_utf16(git_buf_cstr(&buf));
+ if (!ct)
+ goto on_error;
+
+ if (WinHttpAddRequestHeaders(t->request, ct, (ULONG) -1L, WINHTTP_ADDREQ_FLAG_ADD) == FALSE) {
+ giterr_set(GITERR_OS, "Failed to add a header to the request");
+ goto on_error;
+ }
+
+ if (!t->parent.check_cert) {
+ int flags = SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID | SECURITY_FLAG_IGNORE_UNKNOWN_CA;
+ if (WinHttpSetOption(t->request, WINHTTP_OPTION_SECURITY_FLAGS, &flags, sizeof(flags)) == FALSE) {
+ giterr_set(GITERR_OS, "Failed to set options to ignore cert errors");
+ goto on_error;
+ }
+ }
+
+ if (WinHttpSendRequest(t->request, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
+ data, content_length, content_length, 0) == FALSE) {
+ giterr_set(GITERR_OS, "Failed to send request");
+ goto on_error;
+ }
+
+ ret = WinHttpReceiveResponse(t->request, NULL);
+ if (ret == FALSE) {
+ giterr_set(GITERR_OS, "Failed to receive response");
+ goto on_error;
+ }
+
+ return 0;
+
+on_error:
+ git_buf_free(&buf);
+ if (t->request)
+ WinHttpCloseHandle(t->request);
+ t->request = NULL;
+ return -1;
+#endif
+}
+
+static int do_connect(transport_http *t)
+{
+#ifndef GIT_WINHTTP
if (t->parent.connected && http_should_keep_alive(&t->parser))
return 0;
- if (gitno_connect((git_transport *) t, host, port) < 0)
+ if (gitno_connect((git_transport *) t, t->host, t->port) < 0)
return -1;
t->parent.connected = 1;
return 0;
+#else
+ wchar_t *ua = L"git/1.0 (libgit2 " WIDEN(LIBGIT2_VERSION) L")";
+ wchar_t *host;
+ int32_t port;
+
+ t->session = WinHttpOpen(ua, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
+ WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
+
+ if (t->session == NULL) {
+ giterr_set(GITERR_OS, "Failed to init WinHTTP");
+ goto on_error;
+ }
+
+ host = gitwin_to_utf16(t->host);
+ if (host == NULL)
+ goto on_error;
+
+ if (git__strtol32(&port, t->port, NULL, 10) < 0)
+ goto on_error;
+
+ t->connection = WinHttpConnect(t->session, host, port, 0);
+ git__free(host);
+ if (t->connection == NULL) {
+ giterr_set(GITERR_OS, "Failed to connect to host");
+ goto on_error;
+ }
+
+ t->parent.connected = 1;
+ return 0;
+
+on_error:
+ if (t->session) {
+ WinHttpCloseHandle(t->session);
+ t->session = NULL;
+ }
+ return -1;
+#endif
}
/*
git_transport *transport = (git_transport *) buf->cb_data;
transport_http *t = (transport_http *) transport;
size_t old_len;
- gitno_buffer inner;
char buffer[2048];
+#ifdef GIT_WINHTTP
+ DWORD recvd;
+#else
+ gitno_buffer inner;
int error;
+#endif
if (t->transfer_finished)
return 0;
+#ifndef GIT_WINHTTP
gitno_buffer_setup(transport, &inner, buffer, sizeof(buffer));
if ((error = gitno_recv(&inner)) < 0)
http_parser_execute(&t->parser, &t->settings, inner.data, inner.offset);
if (t->error < 0)
return t->error;
+#else
+ old_len = buf->offset;
+ if (WinHttpReadData(t->request, buffer, sizeof(buffer), &recvd) == FALSE) {
+ giterr_set(GITERR_OS, "Failed to read data from the network");
+ return t->error = -1;
+ }
+
+ if (buf->len - buf->offset < recvd) {
+ giterr_set(GITERR_NET, "Can't fit data in the buffer");
+ return t->error = -1;
+ }
+
+ memcpy(buf->data + buf->offset, buffer, recvd);
+ buf->offset += recvd;
+#endif
return (int)(buf->offset - old_len);
}
{
transport_http *t = (transport_http *) transport;
+ /* WinHTTP takes care of this for us */
+#ifndef GIT_WINHTTP
http_parser_init(&t->parser, HTTP_RESPONSE);
t->parser.data = t;
t->transfer_finished = 0;
t->settings.on_headers_complete = on_headers_complete;
t->settings.on_body = on_body_fill_buffer;
t->settings.on_message_complete = on_message_complete;
+#endif
gitno_buffer_setup_callback(transport, &transport->buffer, t->buffer, sizeof(t->buffer), http_recv_cb, t);
}
t->service = git__strdup(service);
GITERR_CHECK_ALLOC(t->service);
- if ((ret = do_connect(t, t->host, t->port)) < 0)
+ if ((ret = do_connect(t)) < 0)
goto cleanup;
- /* Generate and send the HTTP request */
- if ((ret = gen_request(&request, t->path, t->host, "GET", service, 0, 1)) < 0) {
- giterr_set(GITERR_NET, "Failed to generate request");
- goto cleanup;
- }
-
-
- if (gitno_send(transport, request.ptr, request.size, 0) < 0)
+ if ((ret = send_request(t, "upload-pack", NULL, 0, 1)) < 0)
goto cleanup;
setup_gitno_buffer(transport);
static int http_negotiation_step(struct git_transport *transport, void *data, size_t len)
{
transport_http *t = (transport_http *) transport;
- git_buf request = GIT_BUF_INIT;
int ret;
/* First, send the data as a HTTP POST request */
- if ((ret = do_connect(t, t->host, t->port)) < 0)
+ if ((ret = do_connect(t)) < 0)
return -1;
- if ((ret = gen_request(&request, t->path, t->host, "POST", "upload-pack", len, 0)) < 0)
- goto on_error;
-
- if ((ret = gitno_send(transport, request.ptr, request.size, 0)) < 0)
- goto on_error;
-
- if ((ret = gitno_send(transport, data, len, 0)) < 0)
- goto on_error;
-
- git_buf_free(&request);
+ if (send_request(t, "upload-pack", data, len, 0) < 0)
+ return -1;
/* Then we need to set up the buffer to grab data from the HTTP response */
setup_gitno_buffer(transport);
return 0;
-
-on_error:
- git_buf_free(&request);
- return -1;
}
static int http_close(git_transport *transport)
{
+#ifndef GIT_WINHTTP
if (gitno_ssl_teardown(transport) < 0)
return -1;
giterr_set(GITERR_OS, "Failed to close the socket: %s", strerror(errno));
return -1;
}
+#else
+ transport_http *t = (transport_http *) transport;
+
+ if (t->request)
+ WinHttpCloseHandle(t->request);
+ if (t->connection)
+ WinHttpCloseHandle(t->connection);
+ if (t->session)
+ WinHttpCloseHandle(t->session);
+#endif
transport->connected = 0;
int git_transport_https(git_transport **out)
{
-#ifdef GIT_SSL
+#if defined(GIT_SSL) || defined(GIT_WINHTTP)
transport_http *t;
if (git_transport_http((git_transport **)&t) < 0)
return -1;