Last active
February 15, 2016 12:32
-
-
Save QiMata/b268639e8c1913bf646f to your computer and use it in GitHub Desktop.
An http client (like .NET's) using folly and proxygen
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "curl_client.hpp" | |
#include <folly/FileUtil.h> | |
#include <folly/String.h> | |
#include <proxygen/lib/http/HTTPCommonHeaders.h> | |
#include <proxygen/lib/http/HTTPMessage.h> | |
#include <proxygen/lib/http/session/HTTPUpstreamSession.h> | |
#include <proxygen/lib/ssl/SSLContextConfig.h> | |
using namespace folly; | |
using namespace proxygen; | |
using namespace std; | |
curl_client::curl_client(folly::EventBase* evb, proxygen::HTTPMethod httpMethod, | |
const proxygen::URL& url, const proxygen::HTTPHeaders& headers) | |
: evb_(evb), httpMethod_(httpMethod), url_(url) | |
{ | |
headers.forEach([this] (const string& header, const string& val) { | |
request_.getHeaders().add(header, val); | |
}); | |
} | |
curl_client::curl_client(folly::EventBase *evb, proxygen::HTTPMethod httpMethod, const proxygen::URL &url, | |
const proxygen::HTTPHeaders &headers, const std::string &request_body) | |
: evb_(evb), httpMethod_(httpMethod), url_(url), request_body_(folly::make_unique<folly::IOBuf>()) | |
{ | |
headers.forEach([this] (const string& header, const string& val) { | |
request_.getHeaders().add(header, val); | |
}); | |
} | |
curl_client::~curl_client() | |
{ | |
} | |
void curl_client::initializeSsl(const string& nextProtos) | |
{ | |
sslContext_ = std::make_shared<folly::SSLContext>(); | |
sslContext_->setOptions(SSL_OP_NO_COMPRESSION); | |
SSLContextConfig config; | |
sslContext_->ciphers(config.sslCiphers); | |
sslContext_->loadTrustedCertificates("/etc/ssl/certs/ca-certificates.crt"); //may not work for not ubuntu | |
list<string> nextProtoList; | |
folly::splitTo<string>(',', nextProtos, std::inserter(nextProtoList, | |
nextProtoList.begin())); | |
sslContext_->setAdvertisedNextProtocols(nextProtoList); | |
} | |
void curl_client::sslHandshakeFollowup(proxygen::HTTPUpstreamSession* session) noexcept | |
{ | |
AsyncSSLSocket* sslSocket = dynamic_cast<AsyncSSLSocket*>(session->getTransport()); | |
const unsigned char* nextProto = nullptr; | |
unsigned nextProtoLength = 0; | |
sslSocket->getSelectedNextProtocol(&nextProto, &nextProtoLength); | |
if (nextProto) { | |
string((const char*)nextProto, nextProtoLength); | |
} | |
// Note: This ssl session can be used by defining a member and setting | |
// something like sslSession_ = sslSocket->getSSLSession() and then | |
// passing it to the connector::connectSSL() method | |
} | |
void curl_client::connectSuccess(proxygen::HTTPUpstreamSession* session) | |
{ | |
if (url_.isSecure()) | |
{ | |
sslHandshakeFollowup(session); | |
} | |
txn_ = session->newTransaction(this); | |
request_.setMethod(httpMethod_); | |
request_.setHTTPVersion(1, 1); | |
request_.setURL(url_.makeRelativeURL()); | |
request_.setSecure(url_.isSecure()); | |
if (!request_.getHeaders().getNumberOfValues(HTTP_HEADER_USER_AGENT)) | |
{ | |
request_.getHeaders().add(HTTP_HEADER_USER_AGENT, "fusethru"); | |
} | |
if (!request_.getHeaders().getNumberOfValues(HTTP_HEADER_HOST)) | |
{ | |
request_.getHeaders().add(HTTP_HEADER_HOST, url_.getHostAndPort()); | |
} | |
if (!request_.getHeaders().getNumberOfValues(HTTP_HEADER_ACCEPT)) | |
{ | |
request_.getHeaders().add("Accept", "*/*"); | |
} | |
request_.dumpMessage(4); | |
txn_->sendHeaders(request_); | |
if (request_body_) | |
{ | |
txn_->sendBody(std::move(request_body_)); | |
request_body_ = std::unique_ptr<folly::IOBuf>(); | |
} | |
txn_->sendEOM(); | |
session->closeWhenIdle(); | |
} | |
void curl_client::connectError(const folly::AsyncSocketException& ex) | |
{ | |
socket_ex_ = ex; | |
} | |
void curl_client::setTransaction(HTTPTransaction*) noexcept {} | |
void curl_client::detachTransaction() noexcept {} | |
void curl_client::onHeadersComplete(unique_ptr<HTTPMessage> msg) noexcept | |
{ | |
message_ = std::move(msg); | |
} | |
void curl_client::onBody(std::unique_ptr<folly::IOBuf> chain) noexcept | |
{ | |
response_body_ = std::move(chain); | |
} | |
void curl_client::onTrailers(std::unique_ptr<HTTPHeaders>) noexcept | |
{ | |
} | |
void curl_client::onEOM() noexcept | |
{ | |
} | |
void curl_client::onUpgrade(UpgradeProtocol) noexcept | |
{ | |
} | |
void curl_client::onError(const HTTPException& error) noexcept | |
{ | |
} | |
void curl_client::onEgressPaused() noexcept | |
{ | |
} | |
void curl_client::onEgressResumed() noexcept | |
{ | |
} | |
const string& curl_client::getServerName() const { | |
const string& res = request_.getHeaders().getSingleOrEmpty(HTTP_HEADER_HOST); | |
if (res.empty()) | |
{ | |
return url_.getHost(); | |
} | |
return res; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef __UTIL__HTTP__CLIENT__CURL__CURL_CLIENT__HPP__ | |
#define __UTIL__HTTP__CLIENT__CURL__CURL_CLIENT__HPP__ | |
#include <boost/optional.hpp> | |
#include <folly/io/async/EventBase.h> | |
#include <folly/io/async/SSLContext.h> | |
#include <proxygen/lib/http/HTTPConnector.h> | |
#include <proxygen/lib/http/session/HTTPTransaction.h> | |
#include <proxygen/lib/utils/URL.h> | |
class curl_client : public proxygen::HTTPConnector::Callback, | |
public proxygen::HTTPTransactionHandler | |
{ | |
public: | |
curl_client(folly::EventBase* evb, proxygen::HTTPMethod httpMethod, | |
const proxygen::URL& url, const proxygen::HTTPHeaders& headers); | |
curl_client(folly::EventBase* evb, proxygen::HTTPMethod httpMethod, | |
const proxygen::URL& url, const proxygen::HTTPHeaders& headers, | |
const std::string& request_body); | |
~curl_client() override; | |
// initial SSL related structures | |
void initializeSsl(const std::string& nextProtos); | |
void sslHandshakeFollowup(proxygen::HTTPUpstreamSession* session) noexcept; | |
// HTTPConnector methods | |
void connectSuccess(proxygen::HTTPUpstreamSession* session) override; | |
void connectError(const folly::AsyncSocketException& ex) override; | |
// HTTPTransactionHandler methods | |
void setTransaction(proxygen::HTTPTransaction* txn) noexcept override; | |
void detachTransaction() noexcept override; | |
void onHeadersComplete(std::unique_ptr<proxygen::HTTPMessage> msg) noexcept override; | |
void onBody(std::unique_ptr<folly::IOBuf> chain) noexcept override; | |
void onTrailers(std::unique_ptr<proxygen::HTTPHeaders> trailers) noexcept override; | |
void onEOM() noexcept override; | |
void onUpgrade(proxygen::UpgradeProtocol protocol) noexcept override; | |
void onError(const proxygen::HTTPException& error) noexcept override; | |
void onEgressPaused() noexcept override; | |
void onEgressResumed() noexcept override; | |
// Getters | |
folly::SSLContextPtr getSSLContext() { return sslContext_; } | |
const std::string& getServerName() const; | |
std::unique_ptr<proxygen::HTTPMessage> get_message() { return std::move(message_); } | |
std::unique_ptr<folly::IOBuf> get_response_body() { return std::move(response_body_); } | |
boost::optional<folly::AsyncSocketException> get_exception() { | |
return socket_ex_; | |
} | |
protected: | |
private: | |
proxygen::HTTPTransaction* txn_{nullptr}; | |
folly::EventBase* evb_{nullptr}; | |
proxygen::HTTPMethod httpMethod_; | |
proxygen::URL url_; | |
proxygen::HTTPMessage request_; | |
folly::SSLContextPtr sslContext_; | |
boost::optional<folly::AsyncSocketException> socket_ex_; | |
std::unique_ptr<folly::IOBuf> request_body_; | |
std::unique_ptr<proxygen::HTTPMessage> message_; | |
std::unique_ptr<folly::IOBuf> response_body_; | |
}; | |
#endif //__UTIL__HTTP__CLIENT__CURL__CURL_CLIENT__HPP__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "http_client.hpp" | |
#include "curl/curl_client.hpp" | |
#include "http_message.hpp" | |
#include <boost/algorithm/string/predicate.hpp> | |
#include <folly/io/async/EventBase.h> | |
#include <folly/io/async/SSLContext.h> | |
#include <folly/SocketAddress.h> | |
#include <proxygen/lib/http/HTTPConnector.h> | |
#include <folly/FileUtil.h> | |
#include <folly/String.h> | |
#include <proxygen/lib/http/HTTPCommonHeaders.h> | |
#include <proxygen/lib/http/HTTPMessage.h> | |
#include <proxygen/lib/http/session/HTTPUpstreamSession.h> | |
#include <proxygen/lib/ssl/SSLContextConfig.h> | |
using namespace proxygen; | |
using namespace folly; | |
http_client::http_client(const std::string& domain) | |
: domain_(domain) | |
{ | |
} | |
http_message http_client::get_async(const std::string& path) | |
{ | |
folly::EventBase evb; | |
return make_request(HTTPMethod::GET,evb,path); | |
} | |
std::string http_client::create_url(const std::string &path) | |
{ | |
if (boost::starts_with(path,"/")) | |
{ | |
return domain_ + path.substr(1); | |
} | |
else | |
{ | |
return domain_ + path; | |
} | |
} | |
http_message http_client::put_async(const std::string &path, const std::string &body) { | |
folly::EventBase evb; | |
return make_request(HTTPMethod::PUT,evb,path,body); | |
} | |
http_message http_client::make_request(proxygen::HTTPMethod method, folly::EventBase& evb, | |
const std::string &path) | |
{ | |
proxygen::URL url(create_url(path)); | |
curl_client curlClient(&evb, method, url, headers); | |
return getMessage(evb, url, curlClient); | |
} | |
http_message http_client::getMessage(EventBase &evb, const URL &url, curl_client &curlClient) const { | |
SocketAddress addr(url.getHost(), url.getPort(), true); | |
HHWheelTimer::UniquePtr timer{ | |
new HHWheelTimer( | |
&evb, | |
std::chrono::milliseconds(HHWheelTimer::DEFAULT_TICK_INTERVAL), | |
TimeoutManager::InternalEnum::NORMAL, | |
std::chrono::milliseconds(5000))}; | |
HTTPConnector connector(&curlClient, timer.get()); | |
static const AsyncSocket::OptionMap opts{{{SOL_SOCKET, SO_REUSEADDR}, 1}}; | |
if (url.isSecure()) { | |
curlClient.initializeSsl("h2,h2-14,spdy/3.1,spdy/3,http/1.1"); | |
connector.connectSSL( | |
&evb, addr, curlClient.getSSLContext(), nullptr, | |
std::chrono::milliseconds(1000), opts, | |
AsyncSocket::anyAddress(), curlClient.getServerName()); | |
} else { | |
connector.connect(&evb, addr, | |
std::chrono::milliseconds(1000), opts); | |
} | |
evb.loop(); | |
http_message message; | |
message.set_body(move(curlClient.get_response_body())); | |
message.message = move(curlClient.get_message()); | |
return message; | |
} | |
http_message http_client::make_request(proxygen::HTTPMethod method, | |
folly::EventBase& evb, | |
const std::string &path, const std::string &body) { | |
proxygen::URL url(create_url(path)); | |
curl_client curlClient(&evb, method, url, headers,body); | |
return getMessage(evb, url, curlClient); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef __UTIL__HTTP__CLIENT__HTTP_CLIENT__HPP__ | |
#define __UTIL__HTTP__CLIENT__HTTP_CLIENT__HPP__ | |
#include <proxygen/lib/http/HTTPMessage.h> | |
#include <proxygen/lib/utils/URL.h> | |
#include <util/http/client/curl/curl_client.hpp> | |
#include "http_message.hpp" | |
class http_client | |
{ | |
public: | |
http_client(const std::string& domain); | |
fusethru::util::http::client::http_message get_async(const std::string& path); | |
fusethru::util::http::client::http_message post_async(const std::string& path,const std::string& body); | |
fusethru::util::http::client::http_message put_async(const std::string& path,const std::string& body); | |
public: | |
proxygen::HTTPHeaders headers; | |
protected: | |
private: | |
std::string create_url(const std::string& path); | |
fusethru::util::http::client::http_message make_request(proxygen::HTTPMethod method,folly::EventBase& evb,const std::string& path); | |
fusethru::util::http::client::http_message make_request(proxygen::HTTPMethod method,folly::EventBase& evb,const std::string& path,const std::string& body); | |
fusethru::util::http::client::http_message getMessage(folly::EventBase &evb, const proxygen::URL &url, | |
fusethru::util::http::client::curl::curl_client &curlClient) const; | |
private: | |
std::string domain_; | |
}; | |
#endif //__UTIL__HTTP__CLIENT__HTTP_CLIENT__HPP__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef __UTIL__HTTP__CLIENT__HTTP_MESSAGE__HPP__ | |
#define __UTIL__HTTP__CLIENT__HTTP_MESSAGE__HPP__ | |
#include <string> | |
#include <memory> | |
#include <proxygen/lib/http/HTTPConnector.h> | |
#include <folly/FBString.h> | |
class http_message | |
{ | |
public: | |
std::unique_ptr<proxygen::HTTPMessage> message; | |
void set_body(std::unique_ptr<folly::IOBuf> body) | |
{ | |
body_.swap(body); | |
} | |
std::unique_ptr<folly::IOBuf> extract_body() | |
{ | |
return std::move(body_); | |
} | |
folly::fbstring get_folly_str_body() | |
{ | |
return body_->moveToFbString(); | |
} | |
std::string get_std_str_body() | |
{ | |
return body_->moveToFbString().toStdString(); | |
} | |
private: | |
std::unique_ptr<folly::IOBuf> body_; | |
}; | |
#endif //__UTIL__HTTP__CLIENT__HTTP_MESSAGE__HPP__ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment