mirror of
https://github.com/pocoproject/poco.git
synced 2025-10-24 17:30:44 +02:00
added https->http redirect, improved redirect support in streamfactory, fixed client cert validation error
This commit is contained in:
@@ -75,17 +75,25 @@ HTTPSStreamFactory::~HTTPSStreamFactory()
|
|||||||
|
|
||||||
std::istream* HTTPSStreamFactory::open(const URI& uri)
|
std::istream* HTTPSStreamFactory::open(const URI& uri)
|
||||||
{
|
{
|
||||||
poco_assert (uri.getScheme() == "https");
|
poco_assert (uri.getScheme() == "https" || uri.getScheme() == "http");
|
||||||
|
|
||||||
URI resolvedURI(uri);
|
URI resolvedURI(uri);
|
||||||
|
URI proxyUri;
|
||||||
HTTPClientSession* pSession = 0;
|
HTTPClientSession* pSession = 0;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
bool retry = false;
|
||||||
int redirects = 0;
|
int redirects = 0;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
pSession = new HTTPSClientSession(resolvedURI.getHost(), resolvedURI.getPort());
|
if (resolvedURI.getScheme() != "http")
|
||||||
pSession->setProxy(_proxyHost, _proxyPort);
|
pSession = new HTTPSClientSession(resolvedURI.getHost(), resolvedURI.getPort());
|
||||||
|
else
|
||||||
|
pSession = new HTTPClientSession(resolvedURI.getHost(), resolvedURI.getPort());
|
||||||
|
if (proxyUri.empty())
|
||||||
|
pSession->setProxy(_proxyHost, _proxyPort);
|
||||||
|
else
|
||||||
|
pSession->setProxy(proxyUri.getHost(), proxyUri.getPort());
|
||||||
std::string path = resolvedURI.getPathAndQuery();
|
std::string path = resolvedURI.getPathAndQuery();
|
||||||
if (path.empty()) path = "/";
|
if (path.empty()) path = "/";
|
||||||
HTTPRequest req(HTTPRequest::HTTP_GET, path, HTTPMessage::HTTP_1_1);
|
HTTPRequest req(HTTPRequest::HTTP_GET, path, HTTPMessage::HTTP_1_1);
|
||||||
@@ -94,20 +102,38 @@ std::istream* HTTPSStreamFactory::open(const URI& uri)
|
|||||||
std::istream& rs = pSession->receiveResponse(res);
|
std::istream& rs = pSession->receiveResponse(res);
|
||||||
bool moved = (res.getStatus() == HTTPResponse::HTTP_MOVED_PERMANENTLY ||
|
bool moved = (res.getStatus() == HTTPResponse::HTTP_MOVED_PERMANENTLY ||
|
||||||
res.getStatus() == HTTPResponse::HTTP_FOUND ||
|
res.getStatus() == HTTPResponse::HTTP_FOUND ||
|
||||||
res.getStatus() == HTTPResponse::HTTP_SEE_OTHER);
|
res.getStatus() == HTTPResponse::HTTP_SEE_OTHER ||
|
||||||
|
res.getStatus() == HTTPResponse::HTTP_TEMPORARY_REDIRECT);
|
||||||
if (moved)
|
if (moved)
|
||||||
{
|
{
|
||||||
resolvedURI.resolve(res.get("Location"));
|
resolvedURI.resolve(res.get("Location"));
|
||||||
delete pSession;
|
//throw URIRedirection(resolvedURI.toString());
|
||||||
|
delete pSession; pSession = 0;
|
||||||
++redirects;
|
++redirects;
|
||||||
|
retry = true;
|
||||||
}
|
}
|
||||||
else if (res.getStatus() == HTTPResponse::HTTP_OK)
|
else if (res.getStatus() == HTTPResponse::HTTP_OK)
|
||||||
{
|
{
|
||||||
return new HTTPResponseStream(rs, pSession);
|
return new HTTPResponseStream(rs, pSession);
|
||||||
}
|
}
|
||||||
else throw HTTPException(res.getReason(), uri.toString());
|
else if (res.getStatus() == HTTPResponse::HTTP_USEPROXY && !retry)
|
||||||
|
{
|
||||||
|
//The requested resource MUST be accessed through the proxy
|
||||||
|
//given by the Location field. The Location field gives the
|
||||||
|
//URI of the proxy. The recipient is expected to repeat this
|
||||||
|
//single request via the proxy. 305 responses MUST only be generated by origin servers.
|
||||||
|
// only use for one single request!
|
||||||
|
proxyUri.resolve(res.get("Location"));
|
||||||
|
delete pSession; pSession = 0;
|
||||||
|
retry = true; //only allow useproxy once
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delete pSession; pSession = 0;
|
||||||
|
throw HTTPException(res.getReason(), uri.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
while (redirects < MAX_REDIRECTS);
|
while (retry && redirects < MAX_REDIRECTS);
|
||||||
throw HTTPException("Too many redirects", uri.toString());
|
throw HTTPException("Too many redirects", uri.toString());
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
|
|||||||
@@ -48,6 +48,7 @@
|
|||||||
#include "Poco/RegularExpression.h"
|
#include "Poco/RegularExpression.h"
|
||||||
#include <openssl/x509v3.h>
|
#include <openssl/x509v3.h>
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
|
||||||
using Poco::IOException;
|
using Poco::IOException;
|
||||||
@@ -66,6 +67,9 @@ namespace Poco {
|
|||||||
namespace Net {
|
namespace Net {
|
||||||
|
|
||||||
|
|
||||||
|
static void getCertNames (X509*, std::string& commonName, std::set<std::string>& DNSNames);
|
||||||
|
|
||||||
|
|
||||||
SecureSocketImpl::SecureSocketImpl():_pBIO(0), _pSSL(0)
|
SecureSocketImpl::SecureSocketImpl():_pBIO(0), _pSSL(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -420,43 +424,10 @@ long SecureSocketImpl::postConnectionCheck(SSLManager::ContextPtr pContext, X509
|
|||||||
return X509_V_ERR_APPLICATION_VERIFICATION;
|
return X509_V_ERR_APPLICATION_VERIFICATION;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ok = false;
|
std::string commonName;
|
||||||
|
std::set<std::string> dnsNames;
|
||||||
if ((extcount = X509_get_ext_count(cert)) > 0)
|
getCertNames(cert, commonName, dnsNames);
|
||||||
{
|
bool ok = (dnsNames.find(hostName) != dnsNames.end());
|
||||||
for (int i = 0; i < extcount && !ok; ++i)
|
|
||||||
{
|
|
||||||
const char* extstr = 0;
|
|
||||||
X509_EXTENSION* ext;
|
|
||||||
ext = X509_get_ext(cert, i);
|
|
||||||
extstr = OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(ext)));
|
|
||||||
|
|
||||||
if (!strcmp(extstr, "subjectAltName"))
|
|
||||||
{
|
|
||||||
X509V3_EXT_METHOD* meth = X509V3_EXT_get(ext);
|
|
||||||
if (!meth)
|
|
||||||
break;
|
|
||||||
|
|
||||||
#if OPENSSL_VERSION_NUMBER >= 0x00908000
|
|
||||||
const unsigned char* pData = ext->value->data;
|
|
||||||
const unsigned char** ppData = &pData;
|
|
||||||
#else
|
|
||||||
unsigned char* pData = ext->value->data;
|
|
||||||
unsigned char** ppData = &pData;
|
|
||||||
#endif
|
|
||||||
STACK_OF(CONF_VALUE)* val = meth->i2v(meth, meth->d2i(0, ppData, ext->value->length), 0);
|
|
||||||
|
|
||||||
for (int j = 0; j < sk_CONF_VALUE_num(val) && !ok; ++j)
|
|
||||||
{
|
|
||||||
CONF_VALUE* nval = sk_CONF_VALUE_value(val, j);
|
|
||||||
if (!strcmp(nval->name, "DNS") && !strcmp(nval->value, host))
|
|
||||||
{
|
|
||||||
ok = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char data[256];
|
char data[256];
|
||||||
if (!ok && (subj = X509_get_subject_name(cert)) && X509_NAME_get_text_by_NID(subj, NID_commonName, data, 256) > 0)
|
if (!ok && (subj = X509_get_subject_name(cert)) && X509_NAME_get_text_by_NID(subj, NID_commonName, data, 256) > 0)
|
||||||
@@ -618,4 +589,48 @@ bool SecureSocketImpl::matchByAlias(const std::string& alias, const HostEntry& h
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
getCertNames (X509 *certificate,
|
||||||
|
std::string& common_name,
|
||||||
|
std::set<std::string>& DNS_names)
|
||||||
|
{
|
||||||
|
DNS_names.clear ();
|
||||||
|
common_name.clear ();
|
||||||
|
if (certificate == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (STACK_OF (GENERAL_NAME) * names = static_cast<STACK_OF (GENERAL_NAME)
|
||||||
|
*>
|
||||||
|
(X509_get_ext_d2i (certificate, NID_subject_alt_name, 0, 0)))
|
||||||
|
{
|
||||||
|
for (int i = 0; i < sk_GENERAL_NAME_num (names); ++i)
|
||||||
|
{
|
||||||
|
const GENERAL_NAME *name = sk_GENERAL_NAME_value (names, i);
|
||||||
|
if (name->type == GEN_DNS)
|
||||||
|
{
|
||||||
|
const char *data = reinterpret_cast<char *>
|
||||||
|
(ASN1_STRING_data (name->d.ia5));
|
||||||
|
size_t len = ASN1_STRING_length (name->d.ia5);
|
||||||
|
DNS_names.insert (std::string (data, len));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GENERAL_NAMES_free (names);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (X509_NAME * subj = X509_get_subject_name (certificate))
|
||||||
|
{
|
||||||
|
char buffer[256];
|
||||||
|
X509_NAME_get_text_by_NID (subj, NID_commonName,
|
||||||
|
buffer, sizeof buffer);
|
||||||
|
common_name = std::string (buffer);
|
||||||
|
if (DNS_names.empty ())
|
||||||
|
{
|
||||||
|
DNS_names.insert (common_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} } // namespace Poco::Net
|
} } // namespace Poco::Net
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "HTTPSClientSessionTest.h"
|
#include "HTTPSClientSessionTest.h"
|
||||||
|
#include "HTTPSClientTestSuite.h"
|
||||||
#include "CppUnit/TestCaller.h"
|
#include "CppUnit/TestCaller.h"
|
||||||
#include "CppUnit/TestSuite.h"
|
#include "CppUnit/TestSuite.h"
|
||||||
#include "Poco/Net/HTTPSClientSession.h"
|
#include "Poco/Net/HTTPSClientSession.h"
|
||||||
@@ -336,7 +337,7 @@ void HTTPSClientSessionTest::testKeepAlive()
|
|||||||
void HTTPSClientSessionTest::testProxy()
|
void HTTPSClientSessionTest::testProxy()
|
||||||
{
|
{
|
||||||
HTTPSTestServer srv;
|
HTTPSTestServer srv;
|
||||||
HTTPSClientSession s("wwws.appinf.com");
|
HTTPSClientSession s(TESTSERVERNAME);
|
||||||
s.setProxy("proxy.aon.at", 8080);
|
s.setProxy("proxy.aon.at", 8080);
|
||||||
HTTPRequest request(HTTPRequest::HTTP_GET, "/");
|
HTTPRequest request(HTTPRequest::HTTP_GET, "/");
|
||||||
s.sendRequest(request);
|
s.sendRequest(request);
|
||||||
@@ -351,7 +352,7 @@ void HTTPSClientSessionTest::testProxy()
|
|||||||
void HTTPSClientSessionTest::testConnectNB()
|
void HTTPSClientSessionTest::testConnectNB()
|
||||||
{
|
{
|
||||||
SecureStreamSocket sock;
|
SecureStreamSocket sock;
|
||||||
sock.connectNB(SocketAddress("server.com", 443));
|
sock.connectNB(SocketAddress(TESTSERVERNAME, 443));
|
||||||
char buf[512];
|
char buf[512];
|
||||||
std::string msg("GET / HTTP/1.0\r\n\r\n");
|
std::string msg("GET / HTTP/1.0\r\n\r\n");
|
||||||
sock.sendBytes(msg.c_str(), (int)msg.length());
|
sock.sendBytes(msg.c_str(), (int)msg.length());
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
#include "CppUnit/TestSuite.h"
|
#include "CppUnit/TestSuite.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define TESTSERVERNAME "secure.appinf.com"
|
||||||
|
|
||||||
class HTTPSClientTestSuite
|
class HTTPSClientTestSuite
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "HTTPSStreamFactoryTest.h"
|
#include "HTTPSStreamFactoryTest.h"
|
||||||
|
#include "HTTPSClientTestSuite.h"
|
||||||
#include "CppUnit/TestCaller.h"
|
#include "CppUnit/TestCaller.h"
|
||||||
#include "CppUnit/TestSuite.h"
|
#include "CppUnit/TestSuite.h"
|
||||||
#include "Poco/Net/HTTPSStreamFactory.h"
|
#include "Poco/Net/HTTPSStreamFactory.h"
|
||||||
@@ -103,7 +104,7 @@ void HTTPSStreamFactoryTest::testProxy()
|
|||||||
{
|
{
|
||||||
HTTPSTestServer server;
|
HTTPSTestServer server;
|
||||||
HTTPSStreamFactory factory("proxy.aon.at", 8080);
|
HTTPSStreamFactory factory("proxy.aon.at", 8080);
|
||||||
URI uri("https://wwws.appinf.com/");
|
URI uri(std::string("https://") + TESTSERVERNAME + "/");
|
||||||
std::auto_ptr<std::istream> pStr(factory.open(uri));
|
std::auto_ptr<std::istream> pStr(factory.open(uri));
|
||||||
std::ostringstream ostr;
|
std::ostringstream ostr;
|
||||||
StreamCopier::copyStream(*pStr.get(), ostr);
|
StreamCopier::copyStream(*pStr.get(), ostr);
|
||||||
|
|||||||
Reference in New Issue
Block a user