Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F134695
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
50 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
Mode
R77 Guitar
Attached
Detach File
Event Timeline