Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
50 KB
Referenced Files
None
Subscribers
None
diff --git a/src/AvatarLoader.cpp b/src/AvatarLoader.cpp
index a0f5f15..9c7a0ed 100644
--- a/src/AvatarLoader.cpp
+++ b/src/AvatarLoader.cpp
@@ -1,161 +1,161 @@
#include "AvatarLoader.h"
#include "BasicMainWindow.h"
#include "MemoryReader.h"
#include "webclient.h"
#include <QCryptographicHash>
#include <QDebug>
#include <QWaitCondition>
namespace {
const int MAX_CACHE_COUNT = 1000;
const int ICON_SIZE = 64;
}
using WebClientPtr = std::shared_ptr<WebClient>;
struct AvatarLoader::Private {
QMutex mutex;
QWaitCondition condition;
std::deque<RequestItem> requested;
std::deque<RequestItem> completed;
BasicMainWindow *mainwindow = nullptr;
WebClientPtr web;
};
AvatarLoader::AvatarLoader()
: m(new Private)
{
}
AvatarLoader::~AvatarLoader()
{
delete m;
}
void AvatarLoader::start(BasicMainWindow *mainwindow)
{
m->mainwindow = mainwindow;
QThread::start();
}
void AvatarLoader::run()
{
m->web = std::make_shared<WebClient>(m->mainwindow->webContext());
while (1) {
if (isInterruptionRequested()) return;
std::deque<RequestItem> requests;
{
QMutexLocker lock(&m->mutex);
if (m->requested.empty()) {
m->condition.wait(&m->mutex);
}
if (!m->requested.empty()) {
std::swap(requests, m->requested);
}
}
for (RequestItem &item : requests) {
if (isInterruptionRequested()) return;
if (strchr(item.email.c_str(), '@')) {
QString id;
{
QCryptographicHash hash(QCryptographicHash::Md5);
hash.addData(item.email.c_str(), item.email.size());
QByteArray ba = hash.result();
char tmp[100];
for (int i = 0; i < ba.size(); i++) {
sprintf(tmp + i * 2, "%02x", ba.data()[i] & 0xff);
}
id = tmp;
}
QString url = "https://www.gravatar.com/avatar/%1?s=%2";
url = url.arg(id).arg(ICON_SIZE);
- if (m->web->get(WebClient::URL(url.toStdString())) == 200) {
+ if (m->web->get(WebClient::Request(url.toStdString())) == 200) {
if (!m->web->response().content.empty()) {
MemoryReader reader(m->web->response().content.data(), m->web->response().content.size());
reader.open(MemoryReader::ReadOnly);
QImage image;
image.load(&reader, nullptr);
int w = image.width();
int h = image.height();
if (w > 0 && h > 0) {
item.image = image;
{
QMutexLocker lock(&m->mutex);
size_t i = m->requested.size();
while (i > 0) {
i--;
if (m->requested[i].email == item.email) {
m->requested.erase(m->requested.begin() + i);
}
}
while (m->completed.size() >= MAX_CACHE_COUNT) {
m->completed.pop_back();
}
m->completed.push_front(item);
}
emit updated();
continue;
}
}
} else {
m->mainwindow->emitWriteLog(QString("Failed to fetch the avatar.\n").toUtf8());
QString msg = QString::fromStdString(m->web->error().message() + '\n');
m->mainwindow->emitWriteLog(msg.toUtf8());
}
}
}
}
}
QIcon AvatarLoader::fetch(std::string const &email, bool request) const
{
QMutexLocker lock(&m->mutex);
RequestItem item;
item.email = email;
for (size_t i = 0; i < m->completed.size(); i++) {
if (item.email == m->completed[i].email) {
item = m->completed[i];
m->completed.erase(m->completed.begin() + i);
m->completed.insert(m->completed.begin(), item);
return QIcon(QPixmap::fromImage(item.image));
}
}
if (request) {
bool waiting = false;
for (RequestItem const &r : m->requested) {
if (item.email == r.email) {
waiting = true;
break;
}
}
if (!waiting) {
m->requested.push_back(item);
m->condition.wakeOne();
}
}
return QIcon();
}
void AvatarLoader::stop()
{
{
QMutexLocker lock(&m->mutex);
requestInterruption();
m->requested.clear();
m->condition.wakeAll();
}
if (!wait(3000)) {
terminate();
}
if (m->web) {
m->web->close();
m->web.reset();
}
m->completed.clear();
}
diff --git a/src/GitHubAPI.cpp b/src/GitHubAPI.cpp
index 071c180..6b2b227 100644
--- a/src/GitHubAPI.cpp
+++ b/src/GitHubAPI.cpp
@@ -1,109 +1,109 @@
#include "GitHubAPI.h"
#include "BasicMainWindow.h"
#include "MemoryReader.h"
#include "charvec.h"
#include "common/misc.h"
#include "urlencode.h"
#include "webclient.h"
#include <QDebug>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <memory>
using WebClientPtr = GitHubAPI::WebClientPtr;
struct GitHubRequestThread::Private {
BasicMainWindow *mainwindow = nullptr;
WebClientPtr web;
};
GitHubRequestThread::GitHubRequestThread()
: m(new Private)
{
}
GitHubRequestThread::~GitHubRequestThread()
{
delete m;
}
void GitHubRequestThread::start(BasicMainWindow *mainwindow)
{
m->mainwindow = mainwindow;
m->web = std::make_shared<WebClient>(m->mainwindow->webContext());
QThread::start();
}
void GitHubRequestThread::run()
{
ok = false;
- if (web()->get(WebClient::URL(url)) == 200) {
+ if (web()->get(WebClient::Request(url)) == 200) {
WebClient::Response const &r = web()->response();
if (!r.content.empty()) {
text = to_stdstr(r.content);
ok = true;
if (callback) {
ok = callback(text);
}
}
} else {
std::string msg = web()->error().message();
if (!msg.empty()) {
m->mainwindow->emitWriteLog(QString::fromStdString("Failed to access the site: " + url + '\n').toUtf8());
QString s = QString::fromStdString(msg + '\n');
m->mainwindow->emitWriteLog(s.toUtf8());
}
}
}
GitHubAPI::WebClientPtr GitHubRequestThread::web()
{
return m->web;
}
QList<GitHubAPI::SearchResultItem> GitHubAPI::searchRepository(std::string const &q)
{
QList<GitHubAPI::SearchResultItem> items;
GitHubRequestThread th;
{
OverrideWaitCursor;
th.url = "https://api.github.com/search/repositories?q=" + q;
th.start(mainwindow_);
while (!th.wait(1)) {
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
}
}
if (th.ok) {
QByteArray ba(th.text.c_str(), th.text.size());
QJsonDocument doc = QJsonDocument::fromJson(ba);
QJsonArray a1 = doc.object().value("items").toArray();
for (QJsonValue const &v1 : a1) {
QJsonObject o1 = v1.toObject();
SearchResultItem item;
auto String = [&](QString const &key){
return o1.value(key).toString().toStdString();
};
item.full_name = String("full_name");
if (!item.full_name.empty()) {
item.description = String("description");
item.html_url = String("html_url");
item.ssh_url = String("ssh_url");
item.clone_url = String("clone_url");
item.score = o1.value("score").toDouble();
items.push_back(item);
}
}
}
std::sort(items.begin(), items.end(), [](SearchResultItem const &l, SearchResultItem const &r){
return l.score > r.score; // 降順
});
return items;
}
diff --git a/src/main.cpp b/src/main.cpp
index e1aaffd..6389ca2 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,167 +1,168 @@
#include "main.h"
#include "ApplicationGlobal.h"
#include "MainWindow.h"
#include "MySettings.h"
+#include "SettingGeneralForm.h"
#include "common/joinpath.h"
#include "common/misc.h"
#include "darktheme/DarkStyle.h"
#include "platform.h"
#include "webclient.h"
#include <QApplication>
#include <QDebug>
#include <QDir>
#include <QMessageBox>
#include <QProxyStyle>
#include <QStandardPaths>
#include <QTranslator>
#include <string>
#ifdef Q_OS_WIN
#include "win32/win32.h"
#include "SettingGeneralForm.h"
#endif
#ifndef APP_GUITAR
#error APP_GUITAR is not defined.
#endif
ApplicationGlobal *global = nullptr;
ApplicationSettings ApplicationSettings::defaultSettings()
{
ApplicationSettings s;
s.proxy_server = "http://squid:3128/";
return s;
}
static bool isHighDpiScalingEnabled()
{
MySettings s;
s.beginGroup("UI");
QVariant v = s.value("EnableHighDpiScaling");
return v.isNull() || v.toBool();
}
void setEnvironmentVariable(QString const &name, QString const &value);
int main(int argc, char *argv[])
{
#ifdef Q_OS_WIN
setEnvironmentVariable("UNICODEMAP_JP", "cp932");
#else
setenv("UNICODEMAP_JP", "cp932", 1);
#endif
ApplicationGlobal g;
global = &g;
global->organization_name = ORGANIZATION_NAME;
global->application_name = APPLICATION_NAME;
global->generic_config_dir = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
global->app_config_dir = global->generic_config_dir / global->organization_name / global->application_name;
global->config_file_path = joinpath(global->app_config_dir, global->application_name + ".ini");
if (!QFileInfo(global->app_config_dir).isDir()) {
QDir().mkpath(global->app_config_dir);
}
if (isHighDpiScalingEnabled()){
#if (QT_VERSION < QT_VERSION_CHECK(5, 6, 0))
qDebug() << "High DPI scaling is not supported";
#else
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
}
QApplication a(argc, argv);
QApplication::setOrganizationName(global->organization_name);
QApplication::setApplicationName(global->application_name);
qRegisterMetaType<RepositoryItem>("RepositoryItem");
{
MySettings s;
s.beginGroup("UI");
global->language_id = s.value("Language").toString();
global->theme_id = s.value("Theme").toString();
if (global->theme_id.compare("dark", Qt::CaseInsensitive) == 0) {
global->theme = createDarkTheme();
} else {
global->theme = createStandardTheme();
}
s.endGroup();
}
QApplication::setStyle(global->theme->newStyle());
if (QApplication::queryKeyboardModifiers() & Qt::ShiftModifier) {
global->start_with_shift_key = true;
}
WebClient::initialize();
bool f_open_here = false;
QStringList args;
for (int i = 1; i < argc; i++) {
std::string arg = argv[i];
if (arg[0] == '-') {
if (arg == "--open-here") {
f_open_here = true;
}
} else {
args.push_back(QString::fromStdString(arg));
}
}
if (global->app_config_dir.isEmpty()) {
QMessageBox::warning(nullptr, qApp->applicationName(), "Preparation of data storage folder failed.");
return 1;
}
// 設定ファイルがないときは、言語の選択をする。
if (!QFileInfo(global->config_file_path).exists()) {
auto langs = SettingGeneralForm::languages();
SettingGeneralForm::execSelectLanguageDialog(nullptr, langs, [](){});
}
QTranslator translator;
{
if (global->language_id.isEmpty() || global->language_id == "en") {
// thru
} else {
QString path = ":/translations/Guitar_" + global->language_id;
bool f = translator.load(path, QApplication::applicationDirPath());
if (!f) {
qDebug() << QString("Failed to load the translation file: %1").arg(path);
}
QApplication::installTranslator(&translator);
}
}
MainWindow w;
global->panel_bg_color = w.palette().color(QPalette::Background);
w.setWindowIcon(QIcon(":/image/guitar.png"));
w.show();
w.shown();
if (f_open_here) {
QString dir = QDir::current().absolutePath();
w.autoOpenRepository(dir);
} else if (args.size() == 1) {
QString dir = args[0] / QString();
if (dir.startsWith("./") || dir.startsWith(".\\")) {
dir = QDir::current().absolutePath() / dir.mid(2);
}
QFileInfo fi(dir);
if (fi.isDir()) {
dir = fi.absolutePath();
w.autoOpenRepository(dir);
}
}
return QApplication::exec();
}
diff --git a/src/webclient.cpp b/src/webclient.cpp
index 48f99f3..45df773 100644
--- a/src/webclient.cpp
+++ b/src/webclient.cpp
@@ -1,1083 +1,1219 @@
#include "webclient.h"
#include <cstring>
#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
#pragma comment(lib, "ws2_32.lib")
#if USE_OPENSSL
#pragma comment(lib, "libeay32.lib")
#pragma comment(lib, "ssleay32.lib")
#endif
typedef SOCKET socket_t;
#pragma warning(disable:4996)
#else
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <netdb.h>
#define closesocket(S) ::close(S)
using socket_t = int;
#define INVALID_SOCKET (-1)
#define SOCKET_ERROR (-1)
#define stricmp(A, B) strcasecmp(A, B)
#define strnicmp(A, B, C) strncasecmp(A, B, C)
#endif
#if USE_OPENSSL
#include <openssl/crypto.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/x509.h>
#else
typedef void SSL;
typedef void SSL_CTX;
#endif
#include <cassert>
#include "charvec.h"
#define USER_AGENT "Generic Web Client"
struct WebContext::Private {
SSL_CTX *ctx = nullptr;
bool use_keep_alive = false;
WebProxy http_proxy;
WebProxy https_proxy;
};
WebClient::URL::URL(std::string const &addr)
{
data.full_request = addr;
char const *str = addr.c_str();
char const *left;
char const *right;
left = str;
right = strstr(left, "://");
if (right) {
data.scheme.assign(str, right - str);
left = right + 3;
}
right = strchr(left, '/');
if (!right) {
right = left + strlen(left);
}
if (right) {
char const *p = strchr(left, ':');
if (p && left < p && p < right) {
int n = 0;
char const *q = p + 1;
while (q < right) {
if (isdigit(*q & 0xff)) {
n = n * 10 + (*q - '0');
} else {
n = -1;
break;
}
q++;
}
data.host.assign(left, p - left);
if (n > 0 && n < 65536) {
data.port = n;
}
} else {
data.host.assign(left, right - left);
}
data.path = right;
}
}
bool WebClient::URL::isssl() const
{
if (scheme() == "https") return true;
if (scheme() == "http") return false;
if (port() == 443) return true;
return false;
}
void WebClientHandler::abort(std::string const &message)
{
throw WebClient::Error(message);
}
struct WebClient::Private {
std::vector<std::string> request_header;
Error error;
- Response response;
+ WebClient::Response response;
WebContext *webcx;
int crlf_state = 0;
size_t content_offset = 0;
std::string last_host_name;
int last_port = 0;
bool keep_alive = false;
socket_t sock = INVALID_SOCKET;
SSL *ssl = nullptr;
};
WebClient::WebClient(WebContext *webcx)
: m(new Private)
{
assert(webcx);
m->webcx = webcx;
}
WebClient::~WebClient()
{
close();
delete m;
}
void WebClient::initialize()
{
#ifdef _WIN32
WSADATA wsaData;
WORD wVersionRequested;
wVersionRequested = MAKEWORD(1, 1);
WSAStartup(wVersionRequested, &wsaData);
atexit(cleanup);
#endif
#if USE_OPENSSL
OpenSSL_add_all_algorithms();
#endif
}
void WebClient::cleanup()
{
#if USE_OPENSSL
ERR_free_strings();
#endif
#ifdef _WIN32
WSACleanup();
#endif
}
void WebClient::reset()
{
- m->request_header.clear();
+// m->request_header.clear();
m->error = Error();
m->response = Response();
m->crlf_state = 0;
m->content_offset = 0;
}
void WebClient::output_debug_string(char const *str)
{
if (0) {
#ifdef _WIN32
OutputDebugStringA(str);
#else
fwrite(str, 1, strlen(str), stderr);
#endif
}
}
void WebClient::output_debug_strings(std::vector<std::string> const &vec)
{
for (std::string const &s : vec) {
output_debug_string((s + '\n').c_str());
}
}
WebClient::Error const &WebClient::error() const
{
return m->error;
}
void WebClient::clear_error()
{
m->error = Error();
}
int WebClient::get_port(URL const *url, char const *scheme, char const *protocol)
{
int port = url->port();
if (port < 1 || port > 65535) {
struct servent *s;
s = getservbyname(url->scheme().c_str(), protocol);
if (s) {
port = ntohs(s->s_port);
} else {
s = getservbyname(scheme, protocol);
if (s) {
port = ntohs(s->s_port);
}
}
if (port < 1 || port > 65535) {
port = 80;
}
}
return port;
}
static inline std::string to_s(size_t n)
{
char tmp[100];
sprintf(tmp, "%u", (int)n);
return tmp;
}
-void WebClient::set_default_header(URL const &url, Post const *post, RequestOption const &opt)
+void WebClient::set_default_header(Request const &req, Post const *post, RequestOption const &opt)
{
std::vector<std::string> header;
auto AddHeader = [&](std::string const &s){
header.push_back(s);
};
- AddHeader("Host: " + url.host());
+ AddHeader("Host: " + req.url.host());
AddHeader("User-Agent: " USER_AGENT);
AddHeader("Accept: */*");
if (opt.keep_alive) {
AddHeader("Connection: keep-alive");
} else {
AddHeader("Connection: close");
}
if (post) {
AddHeader("Content-Length: " + to_s(post->data.size()));
std::string ct = "Content-Type: ";
if (post->content_type.empty()) {
- ct += "application/octet-stream";
- } else if (post->content_type == CT_MULTIPART_FORM_DATA) {
+ ct += ContentType::APPLICATION_OCTET_STREAM;
+ } else if (post->content_type == ContentType::MULTIPART_FORM_DATA) {
ct += post->content_type;
if (!post->boundary.empty()) {
ct += "; boundary=";
ct += post->boundary;
}
} else {
ct += post->content_type;
}
AddHeader(ct);
}
- header.insert(header.end(), m->request_header.begin(), m->request_header.end());
+ if (req.auth.type == Authorization::Basic) {
+ std::string s = req.auth.uid + ':' + req.auth.pwd;
+ AddHeader("Authorization: Basic " + base64_encode(s));
+ }
+// header.insert(header.end(), m->request_header.begin(), m->request_header.end());
+ header.insert(header.end(), req.headers.begin(), req.headers.end());
m->request_header = std::move(header);
}
-std::string WebClient::make_http_request(URL const &url, Post const *post, WebProxy const *proxy, bool https)
+std::string WebClient::make_http_request(Request const &req, Post const *post, WebProxy const *proxy, bool https)
{
std::string str;
str = post ? "POST " : "GET ";
if (proxy && !https) {
- str += url.data.full_request;
+ str += req.url.data.full_request;
str += " HTTP/1.0";
str += "\r\n";
} else {
- str += url.path();
+ str += req.url.path();
str += " HTTP/1.0";
str += "\r\n";
}
for (std::string const &s: m->request_header) {
str += s;
str += "\r\n";
}
str += "\r\n";
return str;
}
void WebClient::parse_http_header(char const *begin, char const *end, std::vector<std::string> *header)
{
if (begin < end) {
char const *left = begin;
char const *right = left;
while (1) {
if (right >= end) {
break;
}
if (*right == '\r' || *right == '\n') {
if (left < right) {
header->push_back(std::string(left, right));
}
if (right + 1 < end && *right == '\r' && right[1] == '\n') {
right++;
}
right++;
if (*right == '\r' || *right == '\n') {
if (right + 1 < end && *right == '\r' && right[1] == '\n') {
right++;
}
right++;
left = right;
break;
}
left = right;
} else {
right++;
}
}
}
}
void WebClient::parse_http_header(char const *begin, char const *end, WebClient::Response *out)
{
*out = Response();
parse_http_header(begin, end, &out->header);
parse_header(&out->header, out);
}
static void send_(socket_t s, char const *ptr, int len)
{
while (len > 0) {
int n = send(s, ptr, len, 0);
if (n < 1 || n > len) {
throw WebClient::Error("send request failed.");
}
ptr += n;
len -= n;
}
}
void WebClient::on_end_header(std::vector<char> const *vec, WebClientHandler *handler)
{
if (vec->empty()) return;
char const *begin = &vec->at(0);
char const *end = begin + vec->size();
parse_http_header(begin, end, &m->response);
if (handler) {
handler->checkHeader(this);
}
}
void WebClient::append(char const *ptr, size_t len, std::vector<char> *out, WebClientHandler *handler)
{
size_t offset = out->size();
out->insert(out->end(), ptr, ptr + len);
if (m->crlf_state < 0) {
// nop
} else {
for (size_t i = 0; i < len; i++) {
int c = ptr[i] & 0xff;
if (c == '\r') {
m->crlf_state |= 1;
} else if (c == '\n') {
m->crlf_state |= 1;
m->crlf_state++;
} else {
m->crlf_state = 0;
}
if (m->crlf_state == 4) {
m->content_offset = offset + i + 1;
on_end_header(out, handler);
m->crlf_state = -1;
break;
}
}
}
if (handler && m->content_offset > 0) {
offset = out->size();
if (offset > m->content_offset) {
size_t len = offset - m->content_offset;
char const *ptr = &out->at(m->content_offset);
handler->checkContent(ptr, len);
}
}
}
static char *stristr(char *str1, char const *str2)
{
size_t len1 = strlen(str1);
size_t len2 = strlen(str2);
for (size_t i = 0; i + len2 <= len1; i++) {
if (strnicmp(str1 + i, str2, len2) == 0) {
return str1 + i;
}
}
return nullptr;
}
class ResponseHeader {
public:
size_t pos = 0;
std::vector<char> line;
int content_length = -1;
bool connection_keep_alive = false;
bool connection_close = false;
int lf = 0;
enum State {
Header,
Content,
};
State state = Header;
void put(int c)
{
pos++;
if (state == Header) {
if (c== '\r' || c == '\n') {
if (!line.empty()) {
line.push_back(0);
char *begin = &line[0];
char *p = strchr(begin, ':');
if (p && *p == ':') {
*p++ = 0;
auto IS = [&](char const *name){ return stricmp(begin, name) == 0; };
if (IS("content-length")) {
content_length = strtol(p, nullptr, 10);
} else if (IS("connection")) {
if (stristr(p, "keep-alive")) {
connection_keep_alive = true;
} else if (stristr(p, "close")) {
connection_close = true;
}
}
}
line.clear();
}
if (c== '\r') {
return;
}
if (c == '\n') {
lf++;
if (lf == 2) {
state = Content;
}
return;
}
}
lf = 0;
line.push_back(c);
}
}
};
void WebClient::receive_(RequestOption const &opt, std::function<int(char *, int)> const &rcv, std::vector<char> *out)
{
char buf[4096];
size_t pos = 0;
ResponseHeader rh;
while (1) {
int n;
if (rh.state == ResponseHeader::Content && rh.content_length >= 0) {
n = rh.pos + rh.content_length - pos;
if (n > (int)sizeof(buf)) {
n = sizeof(buf);
}
if (n < 1) break;
} else {
n = sizeof(buf);
}
n = rcv(buf, n);
if (n < 1) break;
if (0) { // debug
fwrite(buf, 1, n, stderr);
}
append(buf, n, out, opt.handler);
pos += n;
if (rh.state == ResponseHeader::Header) {
for (int i = 0; i < n; i++) {
rh.put(buf[i]);
if (rh.state == ResponseHeader::Content) {
m->keep_alive = rh.connection_keep_alive && !rh.connection_close;
break;
}
}
}
}
}
-bool WebClient::http_get(URL const &request_url, Post const *post, RequestOption const &opt, std::vector<char> *out)
+bool WebClient::http_get(Request const &request, Post const *post, RequestOption const &opt, std::vector<char> *out)
{
clear_error();
out->clear();
- URL server_url;
+ Request server_req;
WebProxy const *proxy = m->webcx->http_proxy();
if (proxy) {
- server_url = URL(proxy->server);
+ server_req = Request(proxy->server);
} else {
- server_url = request_url;
+ server_req = request;
}
- std::string hostname = server_url.host();
- int port = get_port(&server_url, "http", "tcp");
+ std::string hostname = server_req.url.host();
+ int port = get_port(&server_req.url, "http", "tcp");
m->keep_alive = opt.keep_alive && hostname == m->last_host_name && port == m->last_port;
if (!m->keep_alive) close();
if (m->sock == INVALID_SOCKET) {
struct hostent *servhost;
struct sockaddr_in server;
servhost = gethostbyname(hostname.c_str());
if (!servhost) {
throw Error("gethostbyname failed.");
}
memset((char *)&server, 0, sizeof(server));
server.sin_family = AF_INET;
memcpy((char *)&server.sin_addr, servhost->h_addr, servhost->h_length);
server.sin_port = htons(port);
m->sock = socket(AF_INET, SOCK_STREAM, 0);
if (m->sock == INVALID_SOCKET) {
throw Error("socket failed.");
}
if (connect(m->sock, (struct sockaddr*) &server, sizeof(server)) == SOCKET_ERROR) {
throw Error("connect failed.");
}
}
m->last_host_name = hostname;
m->last_port = port;
- set_default_header(request_url, post, opt);
+ set_default_header(request, post, opt);
- std::string request = make_http_request(request_url, post, proxy, false);
+ std::string req = make_http_request(request, post, proxy, false);
- send_(m->sock, request.c_str(), (int)request.size());
+ send_(m->sock, req.c_str(), (int)req.size());
if (post && !post->data.empty()) {
send_(m->sock, (char const *)&post->data[0], (int)post->data.size());
}
m->crlf_state = 0;
m->content_offset = 0;
receive_(opt, [&](char *ptr, int len){
return recv(m->sock, ptr, len, 0);
}, out);
if (!m->keep_alive) close();
return true;
}
-bool WebClient::https_get(const URL &request_url, Post const *post, RequestOption const &opt, std::vector<char> *out)
+bool WebClient::https_get(Request const &request_req, Post const *post, RequestOption const &opt, std::vector<char> *out)
{
#if USE_OPENSSL
auto *sslctx = m->webcx->m->ctx;
if (!m->webcx || !sslctx) {
output_debug_string("SSL context is null.\n");
return false;
}
clear_error();
out->clear();
auto get_ssl_error = []()->std::string{
char tmp[1000];
unsigned long e = ERR_get_error();
ERR_error_string_n(e, tmp, sizeof(tmp));
return tmp;
};
- URL server_url;
+ Request server_req;
WebProxy const *proxy = m->webcx->https_proxy();
if (proxy) {
- server_url = URL(proxy->server);
+ server_req = Request(proxy->server);
} else {
- server_url = request_url;
+ server_req = request_req;
}
- std::string hostname = server_url.host();
- int port = get_port(&server_url, "https", "tcp");
+ std::string hostname = server_req.url.host();
+ int port = get_port(&server_req.url, "https", "tcp");
m->keep_alive = opt.keep_alive && hostname == m->last_host_name && port == m->last_port;
if (!m->keep_alive) close();
socket_t sock = m->sock;
SSL *ssl = m->ssl;
if (sock == INVALID_SOCKET || !ssl) {
int ret;
struct hostent *servhost;
struct sockaddr_in server;
- servhost = gethostbyname(server_url.host().c_str());
+ servhost = gethostbyname(server_req.url.host().c_str());
if (!servhost) {
- throw Error("gethostbyname failed: " + server_url.host());
+ throw Error("gethostbyname failed: " + server_req.url.host());
}
memset((char *)&server, 0, sizeof(server));
server.sin_family = AF_INET;
memcpy((char *)&server.sin_addr, servhost->h_addr, servhost->h_length);
server.sin_port = htons(port);
if (sock == INVALID_SOCKET) {
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET) {
throw Error("socket failed.");
}
if (connect(sock, (struct sockaddr*) &server, sizeof(server)) == SOCKET_ERROR) {
throw Error("connect failed.");
}
}
if (proxy) { // testing
char port[10];
- sprintf(port, ":%u", get_port(&request_url, "https", "tcp"));
+ sprintf(port, ":%u", get_port(&request_req.url, "https", "tcp"));
std::string str = "CONNECT ";
- str += request_url.data.host;
+ str += request_req.url.data.host;
str += port;
str += " HTTP/1.0\r\n\r\n";
send_(sock, str.c_str(), str.size());
char tmp[1000];
int n = recv(sock, tmp, sizeof(tmp), 0);
int i;
for (i = 0; i < n; i++) {
char c = tmp[i];
if (c < 0x20) break;
}
if (i > 0) {
std::string s(tmp, i);
s = "proxy: " + s + '\n';
#ifdef _WIN32
OutputDebugStringA(s.c_str());
#else
fprintf(stderr, "%s", tmp);
#endif
}
}
ssl = SSL_new(sslctx);
if (!ssl) {
throw Error(get_ssl_error());
}
SSL_set_options(ssl, SSL_OP_NO_SSLv2);
SSL_set_options(ssl, SSL_OP_NO_SSLv3);
ret = SSL_set_fd(ssl, sock);
if (ret == 0) {
throw Error(get_ssl_error());
}
RAND_poll();
while (RAND_status() == 0) {
unsigned short rand_ret = rand() % 65536;
RAND_seed(&rand_ret, sizeof(rand_ret));
}
ret = SSL_connect(ssl);
if (ret != 1) {
throw Error(get_ssl_error());
}
std::string cipher = SSL_get_cipher(ssl);
cipher += '\n';
output_debug_string(cipher.c_str());
std::string version = SSL_get_cipher_version(ssl);
version += '\n';
output_debug_string(version.c_str());
X509 *x509 = SSL_get_peer_certificate(ssl);
if (x509) {
#ifndef OPENSSL_NO_SHA1
std::string fingerprint;
for (int i = 0; i < SHA_DIGEST_LENGTH; i++) {
if (i > 0) {
fingerprint += ':';
}
char tmp[10];
sprintf(tmp, "%02X", x509->sha1_hash[i]);
fingerprint += tmp;
}
fingerprint += '\n';
output_debug_string(fingerprint.c_str());
#endif
long l = SSL_get_verify_result(ssl);
if (l == X509_V_OK) {
// ok
} else {
// wrong
std::string err = X509_verify_cert_error_string(l);
err += '\n';
output_debug_string(err.c_str());
}
std::vector<std::string> vec;
auto GETSTRINGS = [](X509_NAME *x509name, std::vector<std::string> *out){
out->clear();
if (x509name) {
int n = X509_NAME_entry_count(x509name);
for (int i = 0; i < n; i++) {
X509_NAME_ENTRY *entry = X509_NAME_get_entry(x509name, i);
ASN1_STRING *asn1str = X509_NAME_ENTRY_get_data(entry);
int asn1len = ASN1_STRING_length(asn1str);
unsigned char *p = ASN1_STRING_data(asn1str);
std::string str((char const *)p, asn1len);
out->push_back(str);
}
}
};
X509_NAME *subject = X509_get_subject_name(x509);
GETSTRINGS(subject, &vec);
output_debug_string("--- subject ---\n");
output_debug_strings(vec);
X509_NAME *issuer = X509_get_issuer_name(x509);
GETSTRINGS(issuer, &vec);
output_debug_string("--- issuer ---\n");
output_debug_strings(vec);
ASN1_TIME *not_before = X509_get_notBefore(x509);
ASN1_TIME *not_after = X509_get_notAfter(x509);
(void)not_before;
(void)not_after;
X509_free(x509);
} else {
// wrong
}
}
m->last_host_name = hostname;
m->last_port = port;
- set_default_header(request_url, post, opt);
+ set_default_header(request_req, post, opt);
- std::string request = make_http_request(request_url, post, proxy, true);
+ std::string request = make_http_request(request_req, post, proxy, true);
auto SEND = [&](char const *ptr, int len){
while (len > 0) {
int n = SSL_write(ssl, ptr, len);
if (n < 1 || n > len) {
throw WebClient::Error(get_ssl_error());
}
ptr += n;
len -= n;
}
};
SEND(request.c_str(), (int)request.size());
if (post && !post->data.empty()) {
SEND((char const *)&post->data[0], (int)post->data.size());
}
m->crlf_state = 0;
m->content_offset = 0;
receive_(opt, [&](char *ptr, int len){
return SSL_read(ssl, ptr, len);
}, out);
m->sock = sock;
m->ssl = ssl;
if (!m->keep_alive) close();
return true;
#endif
return false;
}
-bool WebClient::get(URL const &url, Post const *post, Response *out, WebClientHandler *handler)
+bool WebClient::get(Request const &req, Post const *post, Response *out, WebClientHandler *handler)
{
reset();
try {
if (!m->webcx->m) {
throw Error("WebContext is null.");
}
RequestOption opt;
opt.keep_alive = m->webcx->m->use_keep_alive;
opt.handler = handler;
std::vector<char> res;
- if (url.isssl()) {
+ if (req.url.isssl()) {
#if USE_OPENSSL
- https_get(url, post, opt, &res);
+ https_get(req, post, opt, &res);
#endif
} else {
- http_get(url, post, opt, &res);
+ http_get(req, post, opt, &res);
}
if (!res.empty()) {
char const *begin = &res[0];
char const *end = begin + res.size();
char const *ptr = begin + m->content_offset;
if (ptr < end) {
out->content.assign(ptr, end);
}
}
return true;
} catch (Error const &e) {
m->error = e;
close();
}
*out = Response();
return false;
}
void WebClient::parse_header(std::vector<std::string> const *header, WebClient::Response *res)
{
if (!header->empty()) {
std::string const &line = header->at(0);
char const *begin = line.c_str();
char const *end = begin + line.size();
if (line.size() > 5 && strncmp(line.c_str(), "HTTP/", 5) == 0) {
int state = 0;
res->version.hi = res->version.lo = res->code = 0;
char const *ptr = begin + 5;
while (1) {
int c = 0;
if (ptr < end) {
c = *ptr & 0xff;
}
switch (state) {
case 0:
if (isdigit(c)) {
res->version.hi = res->version.hi * 10 + (c - '0');
} else if (c == '.') {
state = 1;
} else {
state = -1;
}
break;
case 1:
if (isdigit(c)) {
res->version.lo = res->version.lo * 10 + (c - '0');
} else if (isspace(c)) {
state = 2;
} else {
state = -1;
}
break;
case 2:
if (isspace(c)) {
if (res->code != 0) {
state = -1;
}
} else if (isdigit(c)) {
res->code = res->code * 10 + (c - '0');
} else {
state = -1;
}
break;
default:
state = -1;
break;
}
if (c == 0 || state < 0) {
break;
}
ptr++;
}
}
}
}
std::string WebClient::header_value(std::vector<std::string> const *header, std::string const &name)
{
for (size_t i = 1; i < header->size(); i++) {
std::string const &line = header->at(i);
char const *begin = line.c_str();
char const *end = begin + line.size();
char const *colon = strchr(begin, ':');
if (colon) {
if (strnicmp(begin, name.c_str(), name.size()) == 0) {
char const *ptr = colon + 1;
while (ptr < end && isspace(*ptr & 0xff)) ptr++;
return std::string(ptr, end);
}
}
}
return std::string();
}
std::string WebClient::header_value(std::string const &name) const
{
return header_value(&m->response.header, name);
}
std::string WebClient::content_type() const
{
std::string s = header_value("Content-Type");
char const *begin = s.c_str();
char const *end = begin + s.size();
char const *ptr = begin;
while (ptr < end) {
int c = *ptr & 0xff;
if (c == ';' || c < 0x21) break;
ptr++;
}
if (ptr < end) return std::string(begin, ptr);
return s;
}
size_t WebClient::content_length() const
{
return m->response.content.size();
}
char const *WebClient::content_data() const
{
if (m->response.content.empty()) return "";
return &m->response.content[0];
}
-int WebClient::get(URL const &url, WebClientHandler *handler)
+int WebClient::get(Request const &req, WebClientHandler *handler)
{
- get(url, nullptr, &m->response, handler);
+ get(req, nullptr, &m->response, handler);
return m->response.code;
}
-int WebClient::post(URL const &url, Post const *post, WebClientHandler *handler)
+int WebClient::post(Request const &req, Post const *post, WebClientHandler *handler)
{
- get(url, post, &m->response, handler);
+ get(req, post, &m->response, handler);
return m->response.code;
}
void WebClient::close()
{
#if USE_OPENSSL
if (m->ssl) {
SSL_shutdown(m->ssl);
SSL_free(m->ssl);
m->ssl = nullptr;
}
#endif
if (m->sock != INVALID_SOCKET) {
shutdown(m->sock, 2); // SD_BOTH or SHUT_RDWR
closesocket(m->sock);
m->sock = INVALID_SOCKET;
}
}
void WebClient::add_header(std::string const &text)
{
m->request_header.push_back(text);
}
WebClient::Response const &WebClient::response() const
{
return m->response;
}
void WebClient::make_application_www_form_urlencoded(char const *begin, char const *end, WebClient::Post *out)
{
*out = WebClient::Post();
- out->content_type = CT_APPLICATION_X_WWW_FORM_URLENCODED;
+ out->content_type = ContentType::APPLICATION_X_WWW_FORM_URLENCODED;
print(&out->data, begin, end - begin);
}
void WebClient::make_multipart_form_data(std::vector<Part> const &parts, WebClient::Post *out, std::string const &boundary)
{
*out = WebClient::Post();
- out->content_type = CT_MULTIPART_FORM_DATA;
+ out->content_type = ContentType::MULTIPART_FORM_DATA;
out->boundary = boundary;
for (Part const &part : parts) {
print(&out->data, "--");
print(&out->data, out->boundary);
print(&out->data, "\r\n");
if (!part.content_disposition.type.empty()) {
ContentDisposition const &cd = part.content_disposition;
std::string s;
s = "Content-Disposition: ";
s += cd.type;
auto Add = [&s](std::string const &name, std::string const &value){
if (!value.empty()) {
s += "; " + name + "=\"";
s += value;
s += '\"';
}
};
Add("name", cd.name);
Add("filename", cd.filename);
print(&out->data, s);
print(&out->data, "\r\n");
}
if (!part.content_type.empty()) {
print(&out->data, "Content-Type: " + part.content_type + "\r\n");
}
if (!part.content_transfer_encoding.empty()) {
print(&out->data, "Content-Transfer-Encoding: " + part.content_transfer_encoding + "\r\n");
}
print(&out->data, "\r\n");
print(&out->data, part.data, part.size);
print(&out->data, "\r\n");
}
print(&out->data, "--");
print(&out->data, out->boundary);
print(&out->data, "--\r\n");
}
void WebClient::make_multipart_form_data(char const *data, size_t size, WebClient::Post *out, std::string const &boundary)
{
Part part;
part.data = data;
part.size = size;
std::vector<Part> parts;
parts.push_back(part);
make_multipart_form_data(parts, out, boundary);
}
//
WebContext::WebContext()
: m(new Private)
{
#if USE_OPENSSL
SSL_load_error_strings();
SSL_library_init();
m->ctx = SSL_CTX_new(SSLv23_client_method());
#endif
}
WebContext::~WebContext()
{
#if USE_OPENSSL
SSL_CTX_free(m->ctx);
#endif
delete m;
}
void WebContext::set_keep_alive_enabled(bool f)
{
m->use_keep_alive = f;
}
void WebContext::set_http_proxy(std::string const &proxy)
{
m->http_proxy = WebProxy();
m->http_proxy.server = proxy;
}
void WebContext::set_https_proxy(std::string const &proxy)
{
m->https_proxy = WebProxy();
m->https_proxy.server = proxy;
}
const WebProxy *WebContext::http_proxy() const
{
if (!m->http_proxy.empty()) {
return &m->http_proxy;
}
return nullptr;
}
const WebProxy *WebContext::https_proxy() const
{
if (!m->https_proxy.empty()) {
return &m->https_proxy;
}
if (!m->http_proxy.empty()) {
return &m->http_proxy;
}
return nullptr;
}
bool WebContext::load_cacert(char const *path)
{
#if USE_OPENSSL
int r = SSL_CTX_load_verify_locations(m->ctx, path, nullptr);
return r == 1;
#else
return false;
#endif
}
+//
+
+static unsigned char const PAD = '=';
+
+static const unsigned char _encode_table[] = {
+ 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
+ 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
+ 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
+ 0x77, 0x78, 0x79, 0x7a, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2b, 0x2f,
+};
+
+static const unsigned char _decode_table[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
+ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff,
+};
+
+static inline unsigned char enc(int c)
+{
+ return _encode_table[c & 63];
+}
+
+static inline unsigned char dec(int c)
+{
+ return _decode_table[c & 127];
+}
+
+void base64_encode(char const *src, size_t length, std::vector<char> *out)
+{
+ size_t srcpos, dstlen, dstpos;
+
+ dstlen = (length + 2) / 3 * 4;
+ out->resize(dstlen);
+ if (dstlen == 0) {
+ return;
+ }
+ char *dst = &out->at(0);
+ dstpos = 0;
+ for (srcpos = 0; srcpos < length; srcpos += 3) {
+ int v = (unsigned char)src[srcpos] << 16;
+ if (srcpos + 1 < length) {
+ v |= (unsigned char)src[srcpos + 1] << 8;
+ if (srcpos + 2 < length) {
+ v |= (unsigned char)src[srcpos + 2];
+ dst[dstpos + 3] = enc(v);
+ } else {
+ dst[dstpos + 3] = PAD;
+ }
+ dst[dstpos + 2] = enc(v >> 6);
+ } else {
+ dst[dstpos + 2] = PAD;
+ dst[dstpos + 3] = PAD;
+ }
+ dst[dstpos + 1] = enc(v >> 12);
+ dst[dstpos] = enc(v >> 18);
+ dstpos += 4;
+ }
+}
+
+void base64_decode(char const *src, size_t length, std::vector<char> *out)
+{
+ unsigned char const *begin = (unsigned char const *)src;
+ unsigned char const *end = begin + length;
+ unsigned char const *ptr = begin;
+ out->clear();
+ out->reserve(length * 3 / 4);
+ int count = 0;
+ int bits = 0;
+ while (1) {
+ if (isspace(*ptr)) {
+ ptr++;
+ } else {
+ unsigned char c = 0xff;
+ if (ptr < end && *ptr < 0x80) {
+ c = dec(*ptr);
+ }
+ if (c < 0x40) {
+ bits = (bits << 6) | c;
+ count++;
+ } else {
+ if (count < 4) {
+ bits <<= (4 - count) * 6;
+ }
+ c = 0xff;
+ }
+ if (count == 4 || c == 0xff) {
+ if (count >= 2) {
+ out->push_back(bits >> 16);
+ if (count >= 3) {
+ out->push_back(bits >> 8);
+ if (count == 4) {
+ out->push_back(bits);
+ }
+ }
+ }
+ count = 0;
+ bits = 0;
+ if (c == 0xff) {
+ break;
+ }
+ }
+ ptr++;
+ }
+ }
+}
+
+void base64_encode(std::vector<char> const *src, std::vector<char> *out)
+{
+ base64_encode(&src->at(0), src->size(), out);
+}
+
+void base64_decode(std::vector<char> const *src, std::vector<char> *out)
+{
+ base64_decode(&src->at(0), src->size(), out);
+}
+
+void base64_encode(char const *src, std::vector<char> *out)
+{
+ base64_encode((char const *)src, strlen(src), out);
+}
+
+void base64_decode(char const *src, std::vector<char> *out)
+{
+ base64_decode((char const *)src, strlen(src), out);
+}
+
diff --git a/src/webclient.h b/src/webclient.h
index 282b0d4..be4d608 100644
--- a/src/webclient.h
+++ b/src/webclient.h
@@ -1,184 +1,251 @@
#ifndef WEBCLIENT_H_
#define WEBCLIENT_H_
#include <vector>
#include <string>
#include <functional>
#define USE_OPENSSL 1
#define OPENSSL_NO_SHA1 1
class WebContext;
class WebClient;
-#define CT_APPLICATION_X_WWW_FORM_URLENCODED "application/x-www-form-urlencoded"
-#define CT_MULTIPART_FORM_DATA "multipart/form-data"
-
class WebClientHandler {
protected:
- void abort(std::string const &message = std::string());
+ void abort(std::string const &message = {});
public:
virtual ~WebClientHandler() = default;
- virtual void checkHeader(WebClient * /*wc*/)
+ virtual void checkHeader(WebClient *wc)
{
+ (void)wc;
}
- virtual void checkContent(char const * /*ptr*/, size_t /*len*/)
+ virtual void checkContent(char const *ptr, size_t len)
{
+ (void)ptr;
+ (void)len;
}
};
class WebProxy {
public:
std::string server;
bool empty() const
{
return server.empty();
}
};
class WebClient {
public:
+ class ContentType {
+ public:
+ static constexpr const char *APPLICATION_OCTET_STREAM = "application/octet-stream";
+ static constexpr const char *APPLICATION_X_WWW_FORM_URLENCODED = "application/x-www-form-urlencoded";
+ static constexpr const char *MULTIPART_FORM_DATA = "multipart/form-data";
+ };
class URL {
friend class WebClient;
private:
struct Data {
std::string full_request;
std::string scheme;
std::string host;
int port = 0;
std::string path;
} data;
public:
URL() = default;
URL(std::string const &addr);
std::string const &scheme() const { return data.scheme; }
std::string const &host() const { return data.host; }
int port() const { return data.port; }
std::string const &path() const { return data.path; }
bool isssl() const;
};
-
+ struct Authorization {
+ enum Type {
+ None,
+ Basic,
+ } type = None;
+ std::string uid;
+ std::string pwd;
+ };
+ class Request {
+ friend class ::WebClient;
+ private:
+ URL url;
+ Authorization auth;
+ std::vector<std::string> headers;
+ public:
+ Request() = default;
+ Request(std::string const &loc, std::vector<std::string> const &headers = {})
+ : url(loc)
+ , headers(headers)
+ {
+ }
+ void set_location(std::string const &loc)
+ {
+ url = URL(loc);
+ }
+ void set_authorization(Authorization::Type type, std::string const &uid, std::string const &pwd)
+ {
+ auth.type = type;
+ auth.uid = uid;
+ auth.pwd = pwd;
+ }
+ void set_basic_authorization(std::string const &uid, std::string const &pwd)
+ {
+ set_authorization(WebClient::Authorization::Basic, uid, pwd);
+ }
+ };
class Error {
private:
std::string msg_;
public:
Error() = default;
Error(std::string const &message)
: msg_(message)
{
}
virtual ~Error() = default;
std::string message() const
{
return msg_;
}
};
struct Response {
int code = 0;
struct Version {
unsigned int hi = 0;
unsigned int lo = 0;
} version;
std::vector<std::string> header;
std::vector<char> content;
};
struct Post {
std::string content_type;
std::string boundary;
std::vector<char> data;
};
struct ContentDisposition {
std::string type;
std::string name;
std::string filename;
};
struct Part {
char const *data = nullptr;
size_t size = 0;
std::string content_type;
ContentDisposition content_disposition;
std::string content_transfer_encoding;
Part() = default;
- Part(char const *data, size_t size, std::string const &content_type = std::string())
+ Part(char const *data, size_t size, std::string const &content_type = {})
: data(data)
, size(size)
, content_type(content_type)
{
}
void set_content_disposition(ContentDisposition const &cd)
{
content_disposition = cd;
}
};
struct RequestOption {
WebClientHandler *handler = nullptr;
bool keep_alive = true;
};
private:
struct Private;
Private *m;
void clear_error();
static int get_port(URL const *url, char const *scheme, char const *protocol);
- void set_default_header(URL const &url, Post const *post, const RequestOption &opt);
- std::string make_http_request(URL const &url, Post const *post, const WebProxy *proxy, bool https);
+ void set_default_header(const Request &url, Post const *post, const RequestOption &opt);
+ std::string make_http_request(const Request &url, Post const *post, const WebProxy *proxy, bool https);
void parse_http_header(char const *begin, char const *end, std::vector<std::string> *header);
void parse_http_header(char const *begin, char const *end, Response *out);
- bool http_get(URL const &request_url, Post const *post, RequestOption const &opt, std::vector<char> *out);
- bool https_get(URL const &request_url, Post const *post, RequestOption const &opt, std::vector<char> *out);
- bool get(URL const &url, Post const *post, Response *out, WebClientHandler *handler);
+ bool http_get(const Request &request_req, Post const *post, RequestOption const &opt, std::vector<char> *out);
+ bool https_get(const Request &request_url, Post const *post, RequestOption const &opt, std::vector<char> *out);
+ bool get(const Request &req, Post const *post, Response *out, WebClientHandler *handler);
static void parse_header(std::vector<std::string> const *header, WebClient::Response *res);
static std::string header_value(std::vector<std::string> const *header, std::string const &name);
void append(char const *ptr, size_t len, std::vector<char> *out, WebClientHandler *handler);
void on_end_header(const std::vector<char> *vec, WebClientHandler *handler);
void receive_(const RequestOption &opt, std::function<int (char *, int)> const &, std::vector<char> *out);
void output_debug_string(char const *str);
void output_debug_strings(const std::vector<std::string> &vec);
static void cleanup();
void reset();
public:
static void initialize();
WebClient(WebContext *webcx);
~WebClient();
WebClient(WebClient const &) = delete;
void operator = (WebClient const &) = delete;
Error const &error() const;
- int get(URL const &url, WebClientHandler *handler = nullptr);
- int post(URL const &url, Post const *post, WebClientHandler *handler = nullptr);
+ int get(const Request &req, WebClientHandler *handler = nullptr);
+ int post(const Request &req, Post const *post, WebClientHandler *handler = nullptr);
void close();
void add_header(std::string const &text);
Response const &response() const;
std::string header_value(std::string const &name) const;
std::string content_type() const;
size_t content_length() const;
char const *content_data() const;
static void make_application_www_form_urlencoded(char const *begin, char const *end, WebClient::Post *out);
static void make_multipart_form_data(const std::vector<Part> &parts, WebClient::Post *out, std::string const &boundary);
static void make_multipart_form_data(char const *data, size_t size, WebClient::Post *out, std::string const &boundary);
};
class WebContext {
friend class WebClient;
public:
private:
struct Private;
Private *m;
public:
WebContext();
~WebContext();
WebContext(WebContext const &r) = delete;
void operator = (WebContext const &r) = delete;
void set_keep_alive_enabled(bool f);
void set_http_proxy(std::string const &proxy);
void set_https_proxy(std::string const &proxy);
WebProxy const *http_proxy() const;
WebProxy const *https_proxy() const;
bool load_cacert(char const *path);
};
+//
+
+void base64_encode(char const *src, size_t length, std::vector<char> *out);
+void base64_decode(char const *src, size_t length, std::vector<char> *out);
+void base64_encode(std::vector<char> const *src, std::vector<char> *out);
+void base64_decode(std::vector<char> const *src, std::vector<char> *out);
+void base64_encode(char const *src, std::vector<char> *out);
+void base64_decode(char const *src, std::vector<char> *out);
+static inline std::string to_s_(std::vector<char> const *vec)
+{
+ if (!vec || vec->empty()) return std::string();
+ return std::string((char const *)&(*vec)[0], vec->size());
+}
+static inline std::string base64_encode(std::string const &src)
+{
+ std::vector<char> vec;
+ base64_encode((char const *)src.c_str(), src.size(), &vec);
+ return to_s_(&vec);
+}
+static inline std::string base64_decode(std::string const &src)
+{
+ std::vector<char> vec;
+ base64_decode((char const *)src.c_str(), src.size(), &vec);
+ return to_s_(&vec);
+}
+
#endif

File Metadata

Mime Type
text/x-diff
Expires
Thu, Jun 18, 12:23 AM (1 w, 4 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
70493
Default Alt Text
(50 KB)

Event Timeline