campaignd: WIP: implement sending add-ons on Windows

Does not work fully yet because for whatever reason the client believes
that the add-on is 3,7 gigabytes large (and, thus, the download doesn't
end, as campaignd sends signiticantly less than that).
This commit is contained in:
Jyrki Vesterinen 2017-03-04 23:43:59 +02:00
parent 168e514144
commit 09edad8ad9

View file

@ -23,10 +23,16 @@
#include <sys/sendfile.h>
#endif
#ifdef _WIN32
#include <windows.h>
#endif
#include "server_base.hpp"
#include "simple_wml.hpp"
#include "filesystem.hpp"
#include <stdexcept>
template<typename Handler, typename ErrorHandler>
struct handle_doc
{
@ -161,6 +167,100 @@ void async_send_file(socket_ptr socket, const std::string& filename, Handler han
async_write(*socket, buffers, op);
}
#elif defined(_WIN32)
template<typename Handler, typename ErrorHandler>
struct sendfile_op
{
socket_ptr sock_;
HANDLE file_;
OVERLAPPED overlap_;
Handler handler_;
ErrorHandler error_handler_;
bool pending_;
void operator()(boost::system::error_code ec, std::size_t)
{
bool failed = false;
if (!pending_)
{
BOOL success = TransmitFile(sock_->native_handle(), file_, 0, 0, &overlap_, nullptr, 0);
if (!success)
{
int winsock_ec = WSAGetLastError();
if (winsock_ec == WSA_IO_PENDING || winsock_ec == ERROR_IO_PENDING)
{
// The request is pending. Wait until it completes.
pending_ = true;
sock_->async_write_some(boost::asio::null_buffers(), *this);
return;
}
else
{
failed = true;
}
}
}
else
{
DWORD win_ec = GetLastError();
if (win_ec != ERROR_IO_PENDING && win_ec != ERROR_SUCCESS)
{
failed = true;
}
else if (!HasOverlappedIoCompleted(&overlap_))
{
// Keep waiting.
sock_->async_write_some(boost::asio::null_buffers(), *this);
return;
}
}
CloseHandle(file_);
CloseHandle(overlap_.hEvent);
if (!failed)
{
handler_(sock_);
}
else
{
error_handler_(sock_);
}
}
};
template<typename Handler, typename ErrorHandler>
void async_send_file(socket_ptr socket, const std::string& filename, Handler handler, ErrorHandler error_handler)
{
std::vector<boost::asio::const_buffer> buffers;
SetLastError(ERROR_SUCCESS);
size_t filesize = filesystem::file_size(filename);
std::wstring filename_ucs2 = unicode_cast<std::wstring>(filename);
HANDLE in_file = CreateFileW(filename_ucs2.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN, nullptr);
if (GetLastError() != ERROR_SUCCESS)
{
throw std::runtime_error("Failed to open the file");
}
sendfile_op<Handler, ErrorHandler> op = { socket, in_file, OVERLAPPED(), handler, error_handler, false };
HANDLE event = CreateEvent(nullptr, TRUE, TRUE, nullptr);
if (GetLastError() != ERROR_SUCCESS)
{
throw std::runtime_error("Failed to create an event");
}
op.overlap_.hEvent = event;
handle_doc<Handler, ErrorHandler> handle_send_doc(socket, handler, error_handler, filesize, nullptr);
buffers.push_back(boost::asio::buffer(handle_send_doc.data_size->buf, 4));
async_write(*socket, buffers, op);
}
#else
// TODO: Implement this for systems without sendfile()