]> git.proxmox.com Git - libgit2.git/commitdiff
http: use WinHTTP on Windows
authorCarlos Martín Nieto <cmn@dwim.me>
Sun, 26 Aug 2012 17:22:34 +0000 (19:22 +0200)
committerCarlos Martín Nieto <carlos@cmartin.tk>
Fri, 14 Sep 2012 00:13:30 +0000 (02:13 +0200)
Wondows has its own HTTP library. Use that one when possible instead of
our own.

As we don't depend on them anymore, remove the http-parser library from
the Windows build, as well as the search for OpenSSL.

CMakeLists.txt
src/transports/http.c
src/util.c

index 6a0ffdd42a52da2c5c3f99ed05a5fe01520954e9..7a7a943e582be6c9cd0f9177f22767b765a06d30 100644 (file)
@@ -29,7 +29,12 @@ ENDIF()
 # Find required dependencies
 INCLUDE_DIRECTORIES(src include deps/http-parser)
 
-FILE(GLOB SRC_HTTP deps/http-parser/*.c)
+IF (WIN32 AND NOT MINGW)
+       ADD_DEFINITIONS(-DGIT_WINHTTP)
+ELSE ()
+       FIND_PACKAGE(OpenSSL)
+       FILE(GLOB SRC_HTTP deps/http-parser/*.c)
+ENDIF()
 
 # Specify sha1 implementation
 IF (SHA1_TYPE STREQUAL "ppc")
@@ -75,7 +80,7 @@ OPTION (PROFILE "Generate profiling information" OFF)
 
 # Platform specific compilation flags
 IF (MSVC)
-       # Not using __stdcall with the CRT causes problems
+       # Default to stdcall, as that's what the CLR expects and how the Windows API is built
        OPTION (STDCALL "Buildl libgit2 with the __stdcall convention" ON)
 
        SET(CMAKE_C_FLAGS "/W4 /MP /nologo /Zi ${CMAKE_C_FLAGS}")
@@ -106,7 +111,6 @@ IF (NOT CMAKE_BUILD_TYPE)
        SET(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
 ENDIF ()
 
-FIND_PACKAGE(OpenSSL)
 IF (OPENSSL_FOUND)
   ADD_DEFINITIONS(-DGIT_SSL)
   INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
index de33f56ea1923a9489bb872c3d6a583001e383ef..f1619c51f8868caac054db2ebcc87ccd13be9b11 100644 (file)
@@ -4,7 +4,6 @@
  * 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,
@@ -47,6 +53,11 @@ typedef struct {
 #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,
@@ -77,17 +88,158 @@ static int gen_request(git_buf *buf, const char *path, const char *host, const c
        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
 }
 
 /*
@@ -216,13 +368,18 @@ static int http_recv_cb(gitno_buffer *buf)
        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)
@@ -232,6 +389,21 @@ static int http_recv_cb(gitno_buffer *buf)
        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);
 }
@@ -241,6 +413,8 @@ static void setup_gitno_buffer(git_transport *transport)
 {
        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;
@@ -250,6 +424,7 @@ static void setup_gitno_buffer(git_transport *transport)
        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);
 }
@@ -289,17 +464,10 @@ static int http_connect(git_transport *transport, int direction)
        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);
@@ -332,36 +500,24 @@ cleanup:
 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;
 
@@ -369,6 +525,16 @@ static int http_close(git_transport *transport)
                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;
 
@@ -445,7 +611,7 @@ int git_transport_http(git_transport **out)
 
 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;
index 51bf843dec8778289b3db6856eb7c1bcb72be0b5..719714105fc871bbc9b7b0d5efe9fbf5f5e864f9 100644 (file)
@@ -28,7 +28,7 @@ int git_libgit2_capabilities()
 #ifdef GIT_THREADS
                | GIT_CAP_THREADS
 #endif
-#ifdef GIT_SSL
+#if defined(GIT_SSL) || defined(GIT_WINHTTP)
                | GIT_CAP_HTTPS
 #endif
        ;