mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-06 21:01:15 +00:00
Merge pull request #901 from carlosmn/winhttp
http: use WinHTTP on Windows
This commit is contained in:
commit
db066a9655
@ -29,7 +29,12 @@ ENDIF()
|
|||||||
# Find required dependencies
|
# Find required dependencies
|
||||||
INCLUDE_DIRECTORIES(src include deps/http-parser)
|
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
|
# Specify sha1 implementation
|
||||||
IF (SHA1_TYPE STREQUAL "ppc")
|
IF (SHA1_TYPE STREQUAL "ppc")
|
||||||
@ -75,7 +80,7 @@ OPTION (PROFILE "Generate profiling information" OFF)
|
|||||||
|
|
||||||
# Platform specific compilation flags
|
# Platform specific compilation flags
|
||||||
IF (MSVC)
|
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)
|
OPTION (STDCALL "Buildl libgit2 with the __stdcall convention" ON)
|
||||||
|
|
||||||
SET(CMAKE_C_FLAGS "/W4 /MP /nologo /Zi ${CMAKE_C_FLAGS}")
|
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)
|
SET(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
|
||||||
ENDIF ()
|
ENDIF ()
|
||||||
|
|
||||||
FIND_PACKAGE(OpenSSL)
|
|
||||||
IF (OPENSSL_FOUND)
|
IF (OPENSSL_FOUND)
|
||||||
ADD_DEFINITIONS(-DGIT_SSL)
|
ADD_DEFINITIONS(-DGIT_SSL)
|
||||||
INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
|
INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||||
* a Linking Exception. For full terms see the included COPYING file.
|
* a Linking Exception. For full terms see the included COPYING file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "git2.h"
|
#include "git2.h"
|
||||||
#include "http_parser.h"
|
#include "http_parser.h"
|
||||||
@ -20,6 +19,13 @@
|
|||||||
#include "filebuf.h"
|
#include "filebuf.h"
|
||||||
#include "repository.h"
|
#include "repository.h"
|
||||||
#include "protocol.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 {
|
enum last_cb {
|
||||||
NONE,
|
NONE,
|
||||||
@ -47,6 +53,11 @@ typedef struct {
|
|||||||
#ifdef GIT_WIN32
|
#ifdef GIT_WIN32
|
||||||
WSADATA wsd;
|
WSADATA wsd;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef GIT_WINHTTP
|
||||||
|
HINTERNET session;
|
||||||
|
HINTERNET connection;
|
||||||
|
HINTERNET request;
|
||||||
|
#endif
|
||||||
} transport_http;
|
} transport_http;
|
||||||
|
|
||||||
static int gen_request(git_buf *buf, const char *path, const char *host, const char *op,
|
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;
|
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))
|
if (t->parent.connected && http_should_keep_alive(&t->parser))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (gitno_connect((git_transport *) t, host, port) < 0)
|
if (gitno_connect((git_transport *) t, t->host, t->port) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
t->parent.connected = 1;
|
t->parent.connected = 1;
|
||||||
|
|
||||||
return 0;
|
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;
|
git_transport *transport = (git_transport *) buf->cb_data;
|
||||||
transport_http *t = (transport_http *) transport;
|
transport_http *t = (transport_http *) transport;
|
||||||
size_t old_len;
|
size_t old_len;
|
||||||
gitno_buffer inner;
|
|
||||||
char buffer[2048];
|
char buffer[2048];
|
||||||
|
#ifdef GIT_WINHTTP
|
||||||
|
DWORD recvd;
|
||||||
|
#else
|
||||||
|
gitno_buffer inner;
|
||||||
int error;
|
int error;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (t->transfer_finished)
|
if (t->transfer_finished)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
#ifndef GIT_WINHTTP
|
||||||
gitno_buffer_setup(transport, &inner, buffer, sizeof(buffer));
|
gitno_buffer_setup(transport, &inner, buffer, sizeof(buffer));
|
||||||
|
|
||||||
if ((error = gitno_recv(&inner)) < 0)
|
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);
|
http_parser_execute(&t->parser, &t->settings, inner.data, inner.offset);
|
||||||
if (t->error < 0)
|
if (t->error < 0)
|
||||||
return t->error;
|
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);
|
return (int)(buf->offset - old_len);
|
||||||
}
|
}
|
||||||
@ -241,6 +413,8 @@ static void setup_gitno_buffer(git_transport *transport)
|
|||||||
{
|
{
|
||||||
transport_http *t = (transport_http *) transport;
|
transport_http *t = (transport_http *) transport;
|
||||||
|
|
||||||
|
/* WinHTTP takes care of this for us */
|
||||||
|
#ifndef GIT_WINHTTP
|
||||||
http_parser_init(&t->parser, HTTP_RESPONSE);
|
http_parser_init(&t->parser, HTTP_RESPONSE);
|
||||||
t->parser.data = t;
|
t->parser.data = t;
|
||||||
t->transfer_finished = 0;
|
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_headers_complete = on_headers_complete;
|
||||||
t->settings.on_body = on_body_fill_buffer;
|
t->settings.on_body = on_body_fill_buffer;
|
||||||
t->settings.on_message_complete = on_message_complete;
|
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);
|
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);
|
t->service = git__strdup(service);
|
||||||
GITERR_CHECK_ALLOC(t->service);
|
GITERR_CHECK_ALLOC(t->service);
|
||||||
|
|
||||||
if ((ret = do_connect(t, t->host, t->port)) < 0)
|
if ((ret = do_connect(t)) < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
/* Generate and send the HTTP request */
|
if ((ret = send_request(t, "upload-pack", NULL, 0, 1)) < 0)
|
||||||
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)
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
setup_gitno_buffer(transport);
|
setup_gitno_buffer(transport);
|
||||||
@ -332,36 +500,24 @@ cleanup:
|
|||||||
static int http_negotiation_step(struct git_transport *transport, void *data, size_t len)
|
static int http_negotiation_step(struct git_transport *transport, void *data, size_t len)
|
||||||
{
|
{
|
||||||
transport_http *t = (transport_http *) transport;
|
transport_http *t = (transport_http *) transport;
|
||||||
git_buf request = GIT_BUF_INIT;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* First, send the data as a HTTP POST request */
|
/* 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;
|
return -1;
|
||||||
|
|
||||||
if ((ret = gen_request(&request, t->path, t->host, "POST", "upload-pack", len, 0)) < 0)
|
if (send_request(t, "upload-pack", data, len, 0) < 0)
|
||||||
goto on_error;
|
return -1;
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
/* Then we need to set up the buffer to grab data from the HTTP response */
|
/* Then we need to set up the buffer to grab data from the HTTP response */
|
||||||
setup_gitno_buffer(transport);
|
setup_gitno_buffer(transport);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
on_error:
|
|
||||||
git_buf_free(&request);
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int http_close(git_transport *transport)
|
static int http_close(git_transport *transport)
|
||||||
{
|
{
|
||||||
|
#ifndef GIT_WINHTTP
|
||||||
if (gitno_ssl_teardown(transport) < 0)
|
if (gitno_ssl_teardown(transport) < 0)
|
||||||
return -1;
|
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));
|
giterr_set(GITERR_OS, "Failed to close the socket: %s", strerror(errno));
|
||||||
return -1;
|
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;
|
transport->connected = 0;
|
||||||
|
|
||||||
@ -445,7 +611,7 @@ int git_transport_http(git_transport **out)
|
|||||||
|
|
||||||
int git_transport_https(git_transport **out)
|
int git_transport_https(git_transport **out)
|
||||||
{
|
{
|
||||||
#ifdef GIT_SSL
|
#if defined(GIT_SSL) || defined(GIT_WINHTTP)
|
||||||
transport_http *t;
|
transport_http *t;
|
||||||
if (git_transport_http((git_transport **)&t) < 0)
|
if (git_transport_http((git_transport **)&t) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -28,7 +28,7 @@ int git_libgit2_capabilities()
|
|||||||
#ifdef GIT_THREADS
|
#ifdef GIT_THREADS
|
||||||
| GIT_CAP_THREADS
|
| GIT_CAP_THREADS
|
||||||
#endif
|
#endif
|
||||||
#ifdef GIT_SSL
|
#if defined(GIT_SSL) || defined(GIT_WINHTTP)
|
||||||
| GIT_CAP_HTTPS
|
| GIT_CAP_HTTPS
|
||||||
#endif
|
#endif
|
||||||
;
|
;
|
||||||
|
Loading…
Reference in New Issue
Block a user