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