cleanup argument parsing + add socket creation factory
This commit is contained in:
parent
7c2bc2cf7e
commit
0a752e7d18
@ -20,6 +20,7 @@ set( IXWEBSOCKET_SOURCES
|
|||||||
ixwebsocket/IXSocket.cpp
|
ixwebsocket/IXSocket.cpp
|
||||||
ixwebsocket/IXSocketServer.cpp
|
ixwebsocket/IXSocketServer.cpp
|
||||||
ixwebsocket/IXSocketConnect.cpp
|
ixwebsocket/IXSocketConnect.cpp
|
||||||
|
ixwebsocket/IXSocketFactory.cpp
|
||||||
ixwebsocket/IXDNSLookup.cpp
|
ixwebsocket/IXDNSLookup.cpp
|
||||||
ixwebsocket/IXCancellationRequest.cpp
|
ixwebsocket/IXCancellationRequest.cpp
|
||||||
ixwebsocket/IXWebSocket.cpp
|
ixwebsocket/IXWebSocket.cpp
|
||||||
@ -39,6 +40,7 @@ set( IXWEBSOCKET_HEADERS
|
|||||||
ixwebsocket/IXSocket.h
|
ixwebsocket/IXSocket.h
|
||||||
ixwebsocket/IXSocketServer.h
|
ixwebsocket/IXSocketServer.h
|
||||||
ixwebsocket/IXSocketConnect.h
|
ixwebsocket/IXSocketConnect.h
|
||||||
|
ixwebsocket/IXSocketFactory.h
|
||||||
ixwebsocket/IXSetThreadName.h
|
ixwebsocket/IXSetThreadName.h
|
||||||
ixwebsocket/IXDNSLookup.h
|
ixwebsocket/IXDNSLookup.h
|
||||||
ixwebsocket/IXCancellationRequest.h
|
ixwebsocket/IXCancellationRequest.h
|
||||||
|
@ -7,14 +7,7 @@
|
|||||||
#include "IXHttpClient.h"
|
#include "IXHttpClient.h"
|
||||||
#include "IXUrlParser.h"
|
#include "IXUrlParser.h"
|
||||||
#include "IXWebSocketHttpHeaders.h"
|
#include "IXWebSocketHttpHeaders.h"
|
||||||
|
#include "IXSocketFactory.h"
|
||||||
#if defined(__APPLE__) or defined(__linux__)
|
|
||||||
# ifdef __APPLE__
|
|
||||||
# include <ixwebsocket/IXSocketAppleSSL.h>
|
|
||||||
# else
|
|
||||||
# include <ixwebsocket/IXSocketOpenSSL.h>
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@ -34,12 +27,9 @@ namespace ix
|
|||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse HttpClient::request(
|
HttpResponse HttpClient::request(
|
||||||
const std::string& url,
|
|
||||||
const std::string& verb,
|
const std::string& verb,
|
||||||
const WebSocketHttpHeaders& extraHeaders,
|
const std::string& body,
|
||||||
const HttpParameters& httpParameters,
|
HttpRequestArgs args)
|
||||||
bool followRedirects,
|
|
||||||
bool verbose)
|
|
||||||
{
|
{
|
||||||
int code = 0;
|
int code = 0;
|
||||||
WebSocketHttpHeaders headers;
|
WebSocketHttpHeaders headers;
|
||||||
@ -48,68 +38,54 @@ namespace ix
|
|||||||
std::string protocol, host, path, query;
|
std::string protocol, host, path, query;
|
||||||
int port;
|
int port;
|
||||||
|
|
||||||
if (!parseUrl(url, protocol, host, path, query, port))
|
if (!parseUrl(args.url, protocol, host, path, query, port))
|
||||||
{
|
|
||||||
code = 0; // 0 ?
|
|
||||||
std::string errorMsg("Cannot parse url");
|
|
||||||
return std::make_tuple(code, headers, payload, errorMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (protocol == "http")
|
|
||||||
{
|
|
||||||
_socket = std::make_shared<Socket>();
|
|
||||||
}
|
|
||||||
else if (protocol == "https")
|
|
||||||
{
|
|
||||||
# ifdef __APPLE__
|
|
||||||
_socket = std::make_shared<SocketAppleSSL>();
|
|
||||||
# else
|
|
||||||
_socket = std::make_shared<SocketOpenSSL>();
|
|
||||||
# endif
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
code = 0; // 0 ?
|
|
||||||
std::string errorMsg("Bad protocol");
|
|
||||||
return std::make_tuple(code, headers, payload, errorMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string body;
|
|
||||||
if (verb == "POST")
|
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
size_t count = httpParameters.size();
|
ss << "Cannot parse url: " << args.url;
|
||||||
size_t i = 0;
|
return std::make_tuple(code, headers, payload, ss.str());
|
||||||
|
}
|
||||||
|
|
||||||
for (auto&& it : httpParameters)
|
if (protocol != "http" && protocol != "https")
|
||||||
{
|
{
|
||||||
ss << urlEncode(it.first)
|
std::stringstream ss;
|
||||||
<< "="
|
ss << "Invalid protocol: " << protocol
|
||||||
<< urlEncode(it.second);
|
<< " for url " << args.url
|
||||||
|
<< " . Supported protocols are http and https";
|
||||||
|
|
||||||
if (i++ < (count-1))
|
return std::make_tuple(code, headers, payload, ss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tls = protocol == "https";
|
||||||
|
std::string errorMsg;
|
||||||
|
_socket = createSocket(tls, errorMsg);
|
||||||
|
|
||||||
|
if (!_socket)
|
||||||
{
|
{
|
||||||
ss << "&";
|
return std::make_tuple(code, headers, payload, errorMsg);
|
||||||
}
|
|
||||||
}
|
|
||||||
body = ss.str();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build request string
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << verb << " " << path << " HTTP/1.1\r\n";
|
ss << verb << " " << path << " HTTP/1.1\r\n";
|
||||||
ss << "Host: " << host << "\r\n";
|
ss << "Host: " << host << "\r\n";
|
||||||
ss << "User-Agent: ixwebsocket/1.0.0" << "\r\n";
|
ss << "User-Agent: ixwebsocket/1.0.0" << "\r\n";
|
||||||
ss << "Accept: */*" << "\r\n";
|
ss << "Accept: */*" << "\r\n";
|
||||||
|
|
||||||
for (auto&& it : extraHeaders)
|
// Append extra headers
|
||||||
|
for (auto&& it : args.extraHeaders)
|
||||||
{
|
{
|
||||||
ss << it.first << ": " << urlEncode(it.second) << "\r\n";
|
ss << it.first << ": " << it.second << "\r\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verb == "POST")
|
if (verb == "POST")
|
||||||
{
|
{
|
||||||
ss << "Content-Length: " << body.size() << "\r\n";
|
ss << "Content-Length: " << body.size() << "\r\n";
|
||||||
|
|
||||||
|
// Set default Content-Type if unspecified
|
||||||
|
if (args.extraHeaders.find("Content-Type") == args.extraHeaders.end())
|
||||||
|
{
|
||||||
ss << "Content-Type: application/x-www-form-urlencoded" << "\r\n";
|
ss << "Content-Type: application/x-www-form-urlencoded" << "\r\n";
|
||||||
|
}
|
||||||
ss << "\r\n";
|
ss << "\r\n";
|
||||||
ss << body;
|
ss << body;
|
||||||
}
|
}
|
||||||
@ -120,26 +96,24 @@ namespace ix
|
|||||||
|
|
||||||
std::string req(ss.str());
|
std::string req(ss.str());
|
||||||
|
|
||||||
int timeoutSecs = 10;
|
|
||||||
|
|
||||||
std::string errMsg;
|
std::string errMsg;
|
||||||
static std::atomic<bool> requestInitCancellation(false);
|
std::atomic<bool> requestInitCancellation(false);
|
||||||
auto isCancellationRequested =
|
auto isCancellationRequested =
|
||||||
makeCancellationRequestWithTimeout(timeoutSecs, requestInitCancellation);
|
makeCancellationRequestWithTimeout(args.timeoutSecs, requestInitCancellation);
|
||||||
|
|
||||||
bool success = _socket->connect(host, port, errMsg, isCancellationRequested);
|
bool success = _socket->connect(host, port, errMsg, isCancellationRequested);
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
code = 0; // 0 ?
|
std::stringstream ss;
|
||||||
std::string errorMsg("Cannot connect to url");
|
ss << "Cannot connect to url: " << args.url;
|
||||||
return std::make_tuple(code, headers, payload, errorMsg);
|
return std::make_tuple(code, headers, payload, ss.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verbose)
|
if (args.verbose)
|
||||||
{
|
{
|
||||||
std::cout << "Sending " << verb << " request "
|
std::cerr << "Sending " << verb << " request "
|
||||||
<< "to " << host << ":" << port << std::endl
|
<< "to " << host << ":" << port << std::endl
|
||||||
<< "request size: " << req.size() << " bytes"
|
<< "request size: " << req.size() << " bytes" << std::endl
|
||||||
<< "=============" << std::endl
|
<< "=============" << std::endl
|
||||||
<< req
|
<< req
|
||||||
<< "=============" << std::endl
|
<< "=============" << std::endl
|
||||||
@ -148,7 +122,6 @@ namespace ix
|
|||||||
|
|
||||||
if (!_socket->writeBytes(req, isCancellationRequested))
|
if (!_socket->writeBytes(req, isCancellationRequested))
|
||||||
{
|
{
|
||||||
code = 0; // 0 ?
|
|
||||||
std::string errorMsg("Cannot send request");
|
std::string errorMsg("Cannot send request");
|
||||||
return std::make_tuple(code, headers, payload, errorMsg);
|
return std::make_tuple(code, headers, payload, errorMsg);
|
||||||
}
|
}
|
||||||
@ -159,20 +132,12 @@ namespace ix
|
|||||||
|
|
||||||
if (!lineValid)
|
if (!lineValid)
|
||||||
{
|
{
|
||||||
code = 0; // 0 ?
|
|
||||||
std::string errorMsg("Cannot retrieve status line");
|
std::string errorMsg("Cannot retrieve status line");
|
||||||
return std::make_tuple(code, headers, payload, errorMsg);
|
return std::make_tuple(code, headers, payload, errorMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verbose)
|
|
||||||
{
|
|
||||||
std::cout << "first line: " << line << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
code = -1;
|
|
||||||
if (sscanf(line.c_str(), "HTTP/1.1 %d", &code) != 1)
|
if (sscanf(line.c_str(), "HTTP/1.1 %d", &code) != 1)
|
||||||
{
|
{
|
||||||
code = 0; // 0 ?
|
|
||||||
std::string errorMsg("Cannot parse response code from status line");
|
std::string errorMsg("Cannot parse response code from status line");
|
||||||
return std::make_tuple(code, headers, payload, errorMsg);
|
return std::make_tuple(code, headers, payload, errorMsg);
|
||||||
}
|
}
|
||||||
@ -189,7 +154,8 @@ namespace ix
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Redirect ?
|
// Redirect ?
|
||||||
if (code == 301 && followRedirects)
|
// FIXME wrong conditional
|
||||||
|
if ((code == 301 || code == 308) && args.followRedirects)
|
||||||
{
|
{
|
||||||
if (headers.find("location") == headers.end())
|
if (headers.find("location") == headers.end())
|
||||||
{
|
{
|
||||||
@ -198,8 +164,9 @@ namespace ix
|
|||||||
return std::make_tuple(code, headers, payload, errorMsg);
|
return std::make_tuple(code, headers, payload, errorMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Recurse
|
||||||
std::string location = headers["location"];
|
std::string location = headers["location"];
|
||||||
return request(location, verb, extraHeaders, httpParameters, followRedirects, verbose);
|
return request(verb, body, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verb == "HEAD")
|
if (verb == "HEAD")
|
||||||
@ -254,6 +221,12 @@ namespace ix
|
|||||||
ss << std::hex << line;
|
ss << std::hex << line;
|
||||||
ss >> chunkSize;
|
ss >> chunkSize;
|
||||||
|
|
||||||
|
if (args.verbose)
|
||||||
|
{
|
||||||
|
std::cerr << "Reading " << chunkSize << " bytes"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
payload.reserve(payload.size() + chunkSize);
|
payload.reserve(payload.size() + chunkSize);
|
||||||
|
|
||||||
// Read another line
|
// Read another line
|
||||||
@ -283,6 +256,10 @@ namespace ix
|
|||||||
if (chunkSize == 0) break;
|
if (chunkSize == 0) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (code == 204)
|
||||||
|
{
|
||||||
|
; // 204 is NoContent response code
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
code = 0; // 0 ?
|
code = 0; // 0 ?
|
||||||
@ -293,38 +270,26 @@ namespace ix
|
|||||||
return std::make_tuple(code, headers, payload, "");
|
return std::make_tuple(code, headers, payload, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse HttpClient::get(
|
HttpResponse HttpClient::get(HttpRequestArgs args)
|
||||||
const std::string& url,
|
|
||||||
const WebSocketHttpHeaders& extraHeaders,
|
|
||||||
bool followRedirects,
|
|
||||||
bool verbose)
|
|
||||||
{
|
{
|
||||||
return request(url, "GET", extraHeaders,
|
return request("GET", std::string(), args);
|
||||||
HttpParameters(), followRedirects,
|
|
||||||
verbose);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse HttpClient::post(
|
HttpResponse HttpClient::head(HttpRequestArgs args)
|
||||||
const std::string& url,
|
|
||||||
const WebSocketHttpHeaders& extraHeaders,
|
|
||||||
const HttpParameters& httpParameters,
|
|
||||||
bool followRedirects,
|
|
||||||
bool verbose)
|
|
||||||
{
|
{
|
||||||
return request(url, "POST", extraHeaders,
|
return request("HEAD", std::string(), args);
|
||||||
httpParameters, followRedirects,
|
|
||||||
verbose);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse HttpClient::head(
|
HttpResponse HttpClient::post(const HttpParameters& httpParameters,
|
||||||
const std::string& url,
|
HttpRequestArgs args)
|
||||||
const WebSocketHttpHeaders& extraHeaders,
|
|
||||||
bool followRedirects,
|
|
||||||
bool verbose)
|
|
||||||
{
|
{
|
||||||
return request(url, "HEAD", extraHeaders,
|
return request("POST", serializeHttpParameters(httpParameters), args);
|
||||||
HttpParameters(), followRedirects,
|
}
|
||||||
verbose);
|
|
||||||
|
HttpResponse HttpClient::post(const std::string& body,
|
||||||
|
HttpRequestArgs args)
|
||||||
|
{
|
||||||
|
return request("POST", body, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string HttpClient::urlEncode(const std::string& value)
|
std::string HttpClient::urlEncode(const std::string& value)
|
||||||
@ -353,4 +318,24 @@ namespace ix
|
|||||||
|
|
||||||
return escaped.str();
|
return escaped.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string HttpClient::serializeHttpParameters(const HttpParameters& httpParameters)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
size_t count = httpParameters.size();
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
for (auto&& it : httpParameters)
|
||||||
|
{
|
||||||
|
ss << urlEncode(it.first)
|
||||||
|
<< "="
|
||||||
|
<< urlEncode(it.second);
|
||||||
|
|
||||||
|
if (i++ < (count-1))
|
||||||
|
{
|
||||||
|
ss << "&";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,35 +22,35 @@ namespace ix
|
|||||||
using HttpResponse = std::tuple<int, WebSocketHttpHeaders, std::string, std::string>;
|
using HttpResponse = std::tuple<int, WebSocketHttpHeaders, std::string, std::string>;
|
||||||
using HttpParameters = std::map<std::string, std::string>;
|
using HttpParameters = std::map<std::string, std::string>;
|
||||||
|
|
||||||
|
struct HttpRequestArgs
|
||||||
|
{
|
||||||
|
std::string url;
|
||||||
|
WebSocketHttpHeaders extraHeaders;
|
||||||
|
std::string body;
|
||||||
|
int timeoutSecs;
|
||||||
|
bool followRedirects;
|
||||||
|
bool verbose;
|
||||||
|
};
|
||||||
|
|
||||||
class HttpClient {
|
class HttpClient {
|
||||||
public:
|
public:
|
||||||
HttpClient();
|
HttpClient();
|
||||||
~HttpClient();
|
~HttpClient();
|
||||||
|
|
||||||
// Static methods ?
|
HttpResponse get(HttpRequestArgs args);
|
||||||
HttpResponse get(const std::string& url,
|
HttpResponse head(HttpRequestArgs args);
|
||||||
const WebSocketHttpHeaders& extraHeaders,
|
|
||||||
bool followRedirects,
|
|
||||||
bool verbose);
|
|
||||||
|
|
||||||
HttpResponse post(const std::string& url,
|
HttpResponse post(const HttpParameters& httpParameters,
|
||||||
const WebSocketHttpHeaders& extraHeaders,
|
HttpRequestArgs args);
|
||||||
const HttpParameters& httpParameters,
|
HttpResponse post(const std::string& body,
|
||||||
bool followRedirects,
|
HttpRequestArgs args);
|
||||||
bool verbose);
|
|
||||||
|
|
||||||
HttpResponse head(const std::string& url,
|
|
||||||
const WebSocketHttpHeaders& extraHeaders,
|
|
||||||
bool followRedirects,
|
|
||||||
bool verbose);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
HttpResponse request(const std::string& url,
|
HttpResponse request(const std::string& verb,
|
||||||
const std::string& verb,
|
const std::string& body,
|
||||||
const WebSocketHttpHeaders& extraHeaders,
|
HttpRequestArgs args);
|
||||||
const HttpParameters& httpParameters,
|
|
||||||
bool followRedirects,
|
std::string serializeHttpParameters(const HttpParameters& httpParameters);
|
||||||
bool verbose);
|
|
||||||
|
|
||||||
std::string urlEncode(const std::string& value);
|
std::string urlEncode(const std::string& value);
|
||||||
|
|
||||||
|
42
ixwebsocket/IXSocketFactory.cpp
Normal file
42
ixwebsocket/IXSocketFactory.cpp
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* IXSocketFactory.cpp
|
||||||
|
* Author: Benjamin Sergeant
|
||||||
|
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "IXSocketFactory.h"
|
||||||
|
|
||||||
|
#if defined(__APPLE__) or defined(__linux__)
|
||||||
|
# ifdef __APPLE__
|
||||||
|
# include <ixwebsocket/IXSocketAppleSSL.h>
|
||||||
|
# else
|
||||||
|
# include <ixwebsocket/IXSocketOpenSSL.h>
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace ix
|
||||||
|
{
|
||||||
|
std::shared_ptr<Socket> createSocket(bool tls,
|
||||||
|
std::string& errorMsg)
|
||||||
|
{
|
||||||
|
errorMsg.clear();
|
||||||
|
|
||||||
|
if (!tls)
|
||||||
|
{
|
||||||
|
return std::make_shared<Socket>();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef IXWEBSOCKET_USE_TLS
|
||||||
|
# ifdef __APPLE__
|
||||||
|
return std::make_shared<SocketAppleSSL>();
|
||||||
|
# else
|
||||||
|
return std::make_shared<SocketOpenSSL>();
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
errorMsg = "TLS support is not enabled on this platform.";
|
||||||
|
return nullptr;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
ixwebsocket/IXSocketFactory.h
Normal file
17
ixwebsocket/IXSocketFactory.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* IXSocketFactory.h
|
||||||
|
* Author: Benjamin Sergeant
|
||||||
|
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace ix
|
||||||
|
{
|
||||||
|
class Socket;
|
||||||
|
std::shared_ptr<Socket> createSocket(bool tls,
|
||||||
|
std::string& errorMsg);
|
||||||
|
}
|
@ -9,7 +9,7 @@
|
|||||||
#include "IXCancellationRequest.h"
|
#include "IXCancellationRequest.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
@ -17,7 +17,27 @@ namespace ix
|
|||||||
{
|
{
|
||||||
class Socket;
|
class Socket;
|
||||||
|
|
||||||
using WebSocketHttpHeaders = std::unordered_map<std::string, std::string>;
|
struct CaseInsensitiveLess
|
||||||
|
{
|
||||||
|
// Case Insensitive compare_less binary function
|
||||||
|
struct NocaseCompare
|
||||||
|
{
|
||||||
|
bool operator() (const unsigned char& c1, const unsigned char& c2) const
|
||||||
|
{
|
||||||
|
return std::tolower(c1) < std::tolower(c2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool operator() (const std::string & s1, const std::string & s2) const
|
||||||
|
{
|
||||||
|
return std::lexicographical_compare
|
||||||
|
(s1.begin(), s1.end(), // source range
|
||||||
|
s2.begin(), s2.end(), // dest range
|
||||||
|
NocaseCompare()); // comparison
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using WebSocketHttpHeaders = std::map<std::string, std::string, CaseInsensitiveLess>;
|
||||||
|
|
||||||
std::pair<bool, WebSocketHttpHeaders> parseHttpHeaders(
|
std::pair<bool, WebSocketHttpHeaders> parseHttpHeaders(
|
||||||
std::shared_ptr<Socket> socket,
|
std::shared_ptr<Socket> socket,
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "IXWebSocketHandshake.h"
|
#include "IXWebSocketHandshake.h"
|
||||||
#include "IXWebSocketHttpHeaders.h"
|
#include "IXWebSocketHttpHeaders.h"
|
||||||
#include "IXUrlParser.h"
|
#include "IXUrlParser.h"
|
||||||
|
#include "IXSocketFactory.h"
|
||||||
|
|
||||||
#ifdef IXWEBSOCKET_USE_TLS
|
#ifdef IXWEBSOCKET_USE_TLS
|
||||||
# ifdef __APPLE__
|
# ifdef __APPLE__
|
||||||
@ -78,23 +79,23 @@ namespace ix
|
|||||||
std::string("Could not parse URL ") + url);
|
std::string("Could not parse URL ") + url);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (protocol == "wss")
|
if (protocol != "ws" && protocol != "wss")
|
||||||
{
|
{
|
||||||
_socket.reset();
|
std::stringstream ss;
|
||||||
#ifdef IXWEBSOCKET_USE_TLS
|
ss << "Invalid protocol: " << protocol
|
||||||
# ifdef __APPLE__
|
<< " for url " << url
|
||||||
_socket = std::make_shared<SocketAppleSSL>();
|
<< " . Supported protocols are ws and wss";
|
||||||
# else
|
|
||||||
_socket = std::make_shared<SocketOpenSSL>();
|
return WebSocketInitResult(false, 0, ss.str());
|
||||||
# endif
|
|
||||||
#else
|
|
||||||
return WebSocketInitResult(false, 0, "TLS is not supported.");
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
bool tls = protocol == "wss";
|
||||||
|
std::string errorMsg;
|
||||||
|
_socket = createSocket(tls, errorMsg);
|
||||||
|
|
||||||
|
if (!_socket)
|
||||||
{
|
{
|
||||||
_socket.reset();
|
return WebSocketInitResult(false, 0, errorMsg);
|
||||||
_socket = std::make_shared<Socket>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WebSocketHandshake webSocketHandshake(_requestInitCancellation,
|
WebSocketHandshake webSocketHandshake(_requestInitCancellation,
|
||||||
|
50
ws/ws.cpp
50
ws/ws.cpp
@ -1,12 +1,13 @@
|
|||||||
/*
|
/*
|
||||||
* ws.cpp
|
* ws.cpp
|
||||||
* Author: Benjamin Sergeant
|
* Author: Benjamin Sergeant
|
||||||
* Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
|
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//
|
//
|
||||||
// Main drive for websocket utilities
|
// Main driver for websocket utilities
|
||||||
//
|
//
|
||||||
|
#include "ws.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@ -15,34 +16,6 @@
|
|||||||
#include <cli11/CLI11.hpp>
|
#include <cli11/CLI11.hpp>
|
||||||
#include <ixwebsocket/IXSocket.h>
|
#include <ixwebsocket/IXSocket.h>
|
||||||
|
|
||||||
namespace ix
|
|
||||||
{
|
|
||||||
int ws_http_client_main(const std::string& url,
|
|
||||||
const std::string& headers,
|
|
||||||
const std::string& data,
|
|
||||||
bool headersOnly,
|
|
||||||
bool followRedirects);
|
|
||||||
|
|
||||||
int ws_ping_pong_main(const std::string& url);
|
|
||||||
|
|
||||||
int ws_echo_server_main(int port);
|
|
||||||
|
|
||||||
int ws_broadcast_server_main(int port);
|
|
||||||
|
|
||||||
int ws_chat_main(const std::string& url,
|
|
||||||
const std::string& user);
|
|
||||||
|
|
||||||
int ws_connect_main(const std::string& url);
|
|
||||||
|
|
||||||
int ws_receive_main(const std::string& url,
|
|
||||||
bool enablePerMessageDeflate);
|
|
||||||
|
|
||||||
int ws_transfer_main(int port);
|
|
||||||
|
|
||||||
int ws_send_main(const std::string& url,
|
|
||||||
const std::string& path);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
CLI::App app{"ws is a websocket tool"};
|
CLI::App app{"ws is a websocket tool"};
|
||||||
@ -53,9 +26,13 @@ int main(int argc, char** argv)
|
|||||||
std::string user;
|
std::string user;
|
||||||
std::string data;
|
std::string data;
|
||||||
std::string headers;
|
std::string headers;
|
||||||
|
std::string output;
|
||||||
bool headersOnly = false;
|
bool headersOnly = false;
|
||||||
bool followRedirects = false;
|
bool followRedirects = false;
|
||||||
|
bool verbose = false;
|
||||||
|
bool save = false;
|
||||||
int port = 8080;
|
int port = 8080;
|
||||||
|
int connectTimeOutSeconds = 3;
|
||||||
|
|
||||||
CLI::App* sendApp = app.add_subcommand("send", "Send a file");
|
CLI::App* sendApp = app.add_subcommand("send", "Send a file");
|
||||||
sendApp->add_option("url", url, "Connection url")->required();
|
sendApp->add_option("url", url, "Connection url")->required();
|
||||||
@ -84,13 +61,17 @@ int main(int argc, char** argv)
|
|||||||
CLI::App* pingPongApp = app.add_subcommand("ping", "Ping pong");
|
CLI::App* pingPongApp = app.add_subcommand("ping", "Ping pong");
|
||||||
pingPongApp->add_option("url", url, "Connection url")->required();
|
pingPongApp->add_option("url", url, "Connection url")->required();
|
||||||
|
|
||||||
CLI::App* httpClientApp = app.add_subcommand("http_client", "HTTP Client");
|
CLI::App* httpClientApp = app.add_subcommand("curl", "HTTP Client");
|
||||||
httpClientApp->add_option("url", url, "Connection url")->required();
|
httpClientApp->add_option("url", url, "Connection url")->required();
|
||||||
httpClientApp->add_option("-d", data, "Form data")->join();
|
httpClientApp->add_option("-d", data, "Form data")->join();
|
||||||
httpClientApp->add_option("-F", data, "Form data")->join();
|
httpClientApp->add_option("-F", data, "Form data")->join();
|
||||||
httpClientApp->add_option("-H", headers, "Header")->join();
|
httpClientApp->add_option("-H", headers, "Header")->join();
|
||||||
|
httpClientApp->add_option("--output", output, "Output file");
|
||||||
httpClientApp->add_flag("-I", headersOnly, "Header");
|
httpClientApp->add_flag("-I", headersOnly, "Header");
|
||||||
httpClientApp->add_flag("-L", followRedirects, "Header");
|
httpClientApp->add_flag("-L", followRedirects, "Header");
|
||||||
|
httpClientApp->add_flag("-v", verbose, "Verbose");
|
||||||
|
httpClientApp->add_flag("-O", save, "Save to disk");
|
||||||
|
httpClientApp->add_option("--connect-timeout", connectTimeOutSeconds, "Connection timeout");
|
||||||
|
|
||||||
CLI11_PARSE(app, argc, argv);
|
CLI11_PARSE(app, argc, argv);
|
||||||
|
|
||||||
@ -129,10 +110,11 @@ int main(int argc, char** argv)
|
|||||||
{
|
{
|
||||||
return ix::ws_ping_pong_main(url);
|
return ix::ws_ping_pong_main(url);
|
||||||
}
|
}
|
||||||
else if (app.got_subcommand("http_client"))
|
else if (app.got_subcommand("curl"))
|
||||||
{
|
{
|
||||||
std::cout << "data: " << data << std::endl;
|
return ix::ws_http_client_main(url, headers, data,
|
||||||
return ix::ws_http_client_main(url, headers, data, headersOnly, followRedirects);
|
headersOnly, connectTimeOutSeconds,
|
||||||
|
followRedirects, verbose, save, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -6,11 +6,28 @@
|
|||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <fstream>
|
||||||
#include <ixwebsocket/IXHttpClient.h>
|
#include <ixwebsocket/IXHttpClient.h>
|
||||||
#include <ixwebsocket/IXWebSocketHttpHeaders.h>
|
#include <ixwebsocket/IXWebSocketHttpHeaders.h>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
|
std::string extractFilename(const std::string& path)
|
||||||
|
{
|
||||||
|
std::string::size_type idx;
|
||||||
|
|
||||||
|
idx = path.rfind('/');
|
||||||
|
if (idx != std::string::npos)
|
||||||
|
{
|
||||||
|
std::string filename = path.substr(idx+1);
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
WebSocketHttpHeaders parseHeaders(const std::string& data)
|
WebSocketHttpHeaders parseHeaders(const std::string& data)
|
||||||
{
|
{
|
||||||
WebSocketHttpHeaders headers;
|
WebSocketHttpHeaders headers;
|
||||||
@ -29,7 +46,7 @@ namespace ix
|
|||||||
auto key = token.substr(0, pos);
|
auto key = token.substr(0, pos);
|
||||||
auto val = token.substr(pos+2);
|
auto val = token.substr(pos+2);
|
||||||
|
|
||||||
std::cout << key << ": " << val << std::endl;
|
std::cerr << key << ": " << val << std::endl;
|
||||||
headers[key] = val;
|
headers[key] = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,7 +75,7 @@ namespace ix
|
|||||||
auto key = token.substr(0, pos);
|
auto key = token.substr(0, pos);
|
||||||
auto val = token.substr(pos+1);
|
auto val = token.substr(pos+1);
|
||||||
|
|
||||||
std::cout << key << ": " << val << std::endl;
|
std::cerr << key << ": " << val << std::endl;
|
||||||
httpParameters[key] = val;
|
httpParameters[key] = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,31 +86,36 @@ namespace ix
|
|||||||
const std::string& headersData,
|
const std::string& headersData,
|
||||||
const std::string& data,
|
const std::string& data,
|
||||||
bool headersOnly,
|
bool headersOnly,
|
||||||
bool followRedirects)
|
int timeoutSecs,
|
||||||
|
bool followRedirects,
|
||||||
|
bool verbose,
|
||||||
|
bool save,
|
||||||
|
const std::string& output)
|
||||||
{
|
{
|
||||||
|
HttpRequestArgs args;
|
||||||
|
args.url = url;
|
||||||
|
args.extraHeaders = parseHeaders(headersData);
|
||||||
|
args.timeoutSecs = timeoutSecs;
|
||||||
|
args.followRedirects = followRedirects;
|
||||||
|
args.verbose = verbose;
|
||||||
|
|
||||||
HttpParameters httpParameters = parsePostParameters(data);
|
HttpParameters httpParameters = parsePostParameters(data);
|
||||||
WebSocketHttpHeaders headers = parseHeaders(headersData);
|
|
||||||
|
|
||||||
HttpClient httpClient;
|
HttpClient httpClient;
|
||||||
bool verbose = true;
|
|
||||||
HttpResponse out;
|
HttpResponse out;
|
||||||
if (headersOnly)
|
if (headersOnly)
|
||||||
{
|
{
|
||||||
out = httpClient.head(url, headers,
|
out = httpClient.head(args);
|
||||||
followRedirects, verbose);
|
|
||||||
}
|
}
|
||||||
else if (data.empty())
|
else if (data.empty())
|
||||||
{
|
{
|
||||||
out = httpClient.get(url, headers,
|
out = httpClient.get(args);
|
||||||
followRedirects, verbose);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
out = httpClient.post(url, headers,
|
out = httpClient.post(httpParameters, args);
|
||||||
httpParameters,
|
|
||||||
followRedirects,
|
|
||||||
verbose);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto errorCode = std::get<0>(out);
|
auto errorCode = std::get<0>(out);
|
||||||
auto responseHeaders = std::get<1>(out);
|
auto responseHeaders = std::get<1>(out);
|
||||||
auto payload = std::get<2>(out);
|
auto payload = std::get<2>(out);
|
||||||
@ -101,19 +123,43 @@ namespace ix
|
|||||||
|
|
||||||
for (auto it : responseHeaders)
|
for (auto it : responseHeaders)
|
||||||
{
|
{
|
||||||
std::cout << it.first << ": " << it.second << std::endl;
|
std::cerr << it.first << ": " << it.second << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "error code: " << errorCode << std::endl;
|
std::cerr << "error code: " << errorCode << std::endl;
|
||||||
if (!errorMsg.empty())
|
if (!errorMsg.empty())
|
||||||
{
|
{
|
||||||
std::cout << "error message: " << errorMsg << std::endl;
|
std::cerr << "error message: " << errorMsg << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!headersOnly)
|
if (!headersOnly && errorCode == 200)
|
||||||
|
{
|
||||||
|
if (responseHeaders["Content-Type"] != "application/octet-stream")
|
||||||
{
|
{
|
||||||
std::cout << "payload: " << payload << std::endl;
|
std::cout << "payload: " << payload << std::endl;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << "Binary output can mess up your terminal." << std::endl;
|
||||||
|
std::cerr << "Use the -O flag to save the file to disk." << std::endl;
|
||||||
|
std::cerr << "You can also use the --output option to specify a filename." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (save || !output.empty())
|
||||||
|
{
|
||||||
|
// FIMXE we should decode the url first
|
||||||
|
std::string filename = extractFilename(url);
|
||||||
|
if (!output.empty())
|
||||||
|
{
|
||||||
|
filename = output;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Writing to disk: " << filename << std::endl;
|
||||||
|
std::ofstream out(filename);
|
||||||
|
out.write((char*)&payload.front(), payload.size());
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user