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