1//
2// HTTPSStreamFactory.cpp
3//
4// Library: NetSSL_OpenSSL
5// Package: HTTPSClient
6// Module: HTTPSStreamFactory
7//
8// Copyright (c) 2006-2012, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/Net/HTTPSStreamFactory.h"
16#include "Poco/Net/HTTPSClientSession.h"
17#include "Poco/Net/HTTPIOStream.h"
18#include "Poco/Net/HTTPRequest.h"
19#include "Poco/Net/HTTPResponse.h"
20#include "Poco/Net/HTTPCredentials.h"
21#include "Poco/Net/NetException.h"
22#include "Poco/URI.h"
23#include "Poco/URIStreamOpener.h"
24#include "Poco/UnbufferedStreamBuf.h"
25#include "Poco/NullStream.h"
26#include "Poco/StreamCopier.h"
27#include "Poco/Format.h"
28
29
30using Poco::URIStreamFactory;
31using Poco::URI;
32using Poco::URIStreamOpener;
33using Poco::UnbufferedStreamBuf;
34
35
36namespace Poco {
37namespace Net {
38
39
40HTTPSStreamFactory::HTTPSStreamFactory():
41 _proxyPort(HTTPSession::HTTP_PORT)
42{
43}
44
45
46HTTPSStreamFactory::HTTPSStreamFactory(const std::string& proxyHost, Poco::UInt16 proxyPort):
47 _proxyHost(proxyHost),
48 _proxyPort(proxyPort)
49{
50}
51
52
53HTTPSStreamFactory::HTTPSStreamFactory(const std::string& proxyHost, Poco::UInt16 proxyPort, const std::string& proxyUsername, const std::string& proxyPassword):
54 _proxyHost(proxyHost),
55 _proxyPort(proxyPort),
56 _proxyUsername(proxyUsername),
57 _proxyPassword(proxyPassword)
58{
59}
60
61
62HTTPSStreamFactory::~HTTPSStreamFactory()
63{
64}
65
66
67std::istream* HTTPSStreamFactory::open(const URI& uri)
68{
69 poco_assert (uri.getScheme() == "https" || uri.getScheme() == "http");
70
71 URI resolvedURI(uri);
72 URI proxyUri;
73 HTTPClientSession* pSession = 0;
74 HTTPResponse res;
75 try
76 {
77 bool retry = false;
78 bool authorize = false;
79 int redirects = 0;
80 std::string username;
81 std::string password;
82
83 do
84 {
85 if (!pSession)
86 {
87 if (resolvedURI.getScheme() != "http")
88 pSession = new HTTPSClientSession(resolvedURI.getHost(), resolvedURI.getPort());
89 else
90 pSession = new HTTPClientSession(resolvedURI.getHost(), resolvedURI.getPort());
91
92 if (proxyUri.empty())
93 {
94 if (!_proxyHost.empty())
95 {
96 pSession->setProxy(_proxyHost, _proxyPort);
97 pSession->setProxyCredentials(_proxyUsername, _proxyPassword);
98 }
99 }
100 else
101 {
102 pSession->setProxy(proxyUri.getHost(), proxyUri.getPort());
103 if (!_proxyUsername.empty())
104 {
105 pSession->setProxyCredentials(_proxyUsername, _proxyPassword);
106 }
107 }
108 }
109 std::string path = resolvedURI.getPathAndQuery();
110 if (path.empty()) path = "/";
111 HTTPRequest req(HTTPRequest::HTTP_GET, path, HTTPMessage::HTTP_1_1);
112
113 if (authorize)
114 {
115 HTTPCredentials::extractCredentials(uri, username, password);
116 HTTPCredentials cred(username, password);
117 cred.authenticate(req, res);
118 }
119
120 req.set("User-Agent", Poco::format("poco/%d.%d.%d",
121 (POCO_VERSION >> 24) & 0xFF,
122 (POCO_VERSION >> 16) & 0xFF,
123 (POCO_VERSION >> 8) & 0xFF));
124 req.set("Accept", "*/*");
125
126 pSession->sendRequest(req);
127 std::istream& rs = pSession->receiveResponse(res);
128 bool moved = (res.getStatus() == HTTPResponse::HTTP_MOVED_PERMANENTLY ||
129 res.getStatus() == HTTPResponse::HTTP_FOUND ||
130 res.getStatus() == HTTPResponse::HTTP_SEE_OTHER ||
131 res.getStatus() == HTTPResponse::HTTP_TEMPORARY_REDIRECT);
132 if (moved)
133 {
134 resolvedURI.resolve(res.get("Location"));
135 if (!username.empty())
136 {
137 resolvedURI.setUserInfo(username + ":" + password);
138 authorize = false;
139 }
140 delete pSession;
141 pSession = 0;
142 ++redirects;
143 retry = true;
144 }
145 else if (res.getStatus() == HTTPResponse::HTTP_OK)
146 {
147 return new HTTPResponseStream(rs, pSession);
148 }
149 else if (res.getStatus() == HTTPResponse::HTTP_USEPROXY && !retry)
150 {
151 // The requested resource MUST be accessed through the proxy
152 // given by the Location field. The Location field gives the
153 // URI of the proxy. The recipient is expected to repeat this
154 // single request via the proxy. 305 responses MUST only be generated by origin servers.
155 // only use for one single request!
156 proxyUri.resolve(res.get("Location"));
157 delete pSession;
158 pSession = 0;
159 retry = true; // only allow useproxy once
160 }
161 else if (res.getStatus() == HTTPResponse::HTTP_UNAUTHORIZED && !authorize)
162 {
163 authorize = true;
164 retry = true;
165 Poco::NullOutputStream null;
166 Poco::StreamCopier::copyStream(rs, null);
167 }
168 else throw HTTPException(res.getReason(), uri.toString());
169 }
170 while (retry && redirects < MAX_REDIRECTS);
171 throw HTTPException("Too many redirects", uri.toString());
172 }
173 catch (...)
174 {
175 delete pSession;
176 throw;
177 }
178}
179
180
181void HTTPSStreamFactory::registerFactory()
182{
183 URIStreamOpener::defaultOpener().registerStreamFactory("https", new HTTPSStreamFactory);
184}
185
186
187void HTTPSStreamFactory::unregisterFactory()
188{
189 URIStreamOpener::defaultOpener().unregisterStreamFactory("https");
190}
191
192
193} } // namespace Poco::Net
194