1//
2// HTTPClientSession.cpp
3//
4// Library: Net
5// Package: HTTPClient
6// Module: HTTPClientSession
7//
8// Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/Net/HTTPClientSession.h"
16#include "Poco/Net/HTTPRequest.h"
17#include "Poco/Net/HTTPResponse.h"
18#include "Poco/Net/HTTPHeaderStream.h"
19#include "Poco/Net/HTTPStream.h"
20#include "Poco/Net/HTTPFixedLengthStream.h"
21#include "Poco/Net/HTTPChunkedStream.h"
22#include "Poco/Net/HTTPBasicCredentials.h"
23#include "Poco/Net/NetException.h"
24#include "Poco/NumberFormatter.h"
25#include "Poco/CountingStream.h"
26#include "Poco/RegularExpression.h"
27#include <sstream>
28
29
30using Poco::NumberFormatter;
31using Poco::IllegalStateException;
32
33
34namespace Poco {
35namespace Net {
36
37
38HTTPClientSession::ProxyConfig HTTPClientSession::_globalProxyConfig;
39
40
41HTTPClientSession::HTTPClientSession():
42 _port(HTTPSession::HTTP_PORT),
43 _proxyConfig(_globalProxyConfig),
44 _keepAliveTimeout(DEFAULT_KEEP_ALIVE_TIMEOUT, 0),
45 _reconnect(false),
46 _mustReconnect(false),
47 _expectResponseBody(false),
48 _responseReceived(false)
49{
50}
51
52
53HTTPClientSession::HTTPClientSession(const StreamSocket& socket):
54 HTTPSession(socket),
55 _port(HTTPSession::HTTP_PORT),
56 _proxyConfig(_globalProxyConfig),
57 _keepAliveTimeout(DEFAULT_KEEP_ALIVE_TIMEOUT, 0),
58 _reconnect(false),
59 _mustReconnect(false),
60 _expectResponseBody(false),
61 _responseReceived(false)
62{
63}
64
65
66HTTPClientSession::HTTPClientSession(const SocketAddress& address):
67 _host(address.host().toString()),
68 _port(address.port()),
69 _proxyConfig(_globalProxyConfig),
70 _keepAliveTimeout(DEFAULT_KEEP_ALIVE_TIMEOUT, 0),
71 _reconnect(false),
72 _mustReconnect(false),
73 _expectResponseBody(false),
74 _responseReceived(false)
75{
76}
77
78
79HTTPClientSession::HTTPClientSession(const std::string& host, Poco::UInt16 port):
80 _host(host),
81 _port(port),
82 _proxyConfig(_globalProxyConfig),
83 _keepAliveTimeout(DEFAULT_KEEP_ALIVE_TIMEOUT, 0),
84 _reconnect(false),
85 _mustReconnect(false),
86 _expectResponseBody(false),
87 _responseReceived(false)
88{
89}
90
91
92HTTPClientSession::HTTPClientSession(const std::string& host, Poco::UInt16 port, const ProxyConfig& proxyConfig):
93 _host(host),
94 _port(port),
95 _proxyConfig(proxyConfig),
96 _keepAliveTimeout(DEFAULT_KEEP_ALIVE_TIMEOUT, 0),
97 _reconnect(false),
98 _mustReconnect(false),
99 _expectResponseBody(false),
100 _responseReceived(false)
101{
102}
103
104
105HTTPClientSession::~HTTPClientSession()
106{
107}
108
109
110void HTTPClientSession::setHost(const std::string& host)
111{
112 if (!connected())
113 _host = host;
114 else
115 throw IllegalStateException("Cannot set the host for an already connected session");
116}
117
118
119void HTTPClientSession::setPort(Poco::UInt16 port)
120{
121 if (!connected())
122 _port = port;
123 else
124 throw IllegalStateException("Cannot set the port number for an already connected session");
125}
126
127
128void HTTPClientSession::setProxy(const std::string& host, Poco::UInt16 port)
129{
130 if (!connected())
131 {
132 _proxyConfig.host = host;
133 _proxyConfig.port = port;
134 }
135 else throw IllegalStateException("Cannot set the proxy host and port for an already connected session");
136}
137
138
139void HTTPClientSession::setProxyHost(const std::string& host)
140{
141 if (!connected())
142 _proxyConfig.host = host;
143 else
144 throw IllegalStateException("Cannot set the proxy host for an already connected session");
145}
146
147
148void HTTPClientSession::setProxyPort(Poco::UInt16 port)
149{
150 if (!connected())
151 _proxyConfig.port = port;
152 else
153 throw IllegalStateException("Cannot set the proxy port number for an already connected session");
154}
155
156
157void HTTPClientSession::setProxyCredentials(const std::string& username, const std::string& password)
158{
159 _proxyConfig.username = username;
160 _proxyConfig.password = password;
161}
162
163
164void HTTPClientSession::setProxyUsername(const std::string& username)
165{
166 _proxyConfig.username = username;
167}
168
169
170void HTTPClientSession::setProxyPassword(const std::string& password)
171{
172 _proxyConfig.password = password;
173}
174
175
176void HTTPClientSession::setProxyConfig(const ProxyConfig& config)
177{
178 _proxyConfig = config;
179}
180
181
182void HTTPClientSession::setGlobalProxyConfig(const ProxyConfig& config)
183{
184 _globalProxyConfig = config;
185}
186
187
188void HTTPClientSession::setKeepAliveTimeout(const Poco::Timespan& timeout)
189{
190 _keepAliveTimeout = timeout;
191}
192
193
194std::ostream& HTTPClientSession::sendRequest(HTTPRequest& request)
195{
196 clearException();
197 _pResponseStream = 0;
198 _responseReceived = false;
199
200 bool keepAlive = getKeepAlive();
201 if (((connected() && !keepAlive) || mustReconnect()) && !_host.empty())
202 {
203 close();
204 _mustReconnect = false;
205 }
206 try
207 {
208 if (!connected())
209 reconnect();
210 if (!keepAlive)
211 request.setKeepAlive(false);
212 if (!request.has(HTTPRequest::HOST) && !_host.empty())
213 request.setHost(_host, _port);
214 if (!_proxyConfig.host.empty() && !bypassProxy())
215 {
216 request.setURI(proxyRequestPrefix() + request.getURI());
217 proxyAuthenticate(request);
218 }
219 _reconnect = keepAlive;
220 _expectResponseBody = request.getMethod() != HTTPRequest::HTTP_HEAD;
221 const std::string& method = request.getMethod();
222 if (request.getChunkedTransferEncoding())
223 {
224 HTTPHeaderOutputStream hos(*this);
225 request.write(hos);
226 _pRequestStream = new HTTPChunkedOutputStream(*this);
227 }
228 else if (request.hasContentLength())
229 {
230 Poco::CountingOutputStream cs;
231 request.write(cs);
232#if POCO_HAVE_INT64
233 _pRequestStream = new HTTPFixedLengthOutputStream(*this, request.getContentLength64() + cs.chars());
234#else
235 _pRequestStream = new HTTPFixedLengthOutputStream(*this, request.getContentLength() + cs.chars());
236#endif
237 request.write(*_pRequestStream);
238 }
239 else if ((method != HTTPRequest::HTTP_PUT && method != HTTPRequest::HTTP_POST && method != HTTPRequest::HTTP_PATCH) || request.has(HTTPRequest::UPGRADE))
240 {
241 Poco::CountingOutputStream cs;
242 request.write(cs);
243 _pRequestStream = new HTTPFixedLengthOutputStream(*this, cs.chars());
244 request.write(*_pRequestStream);
245 }
246 else
247 {
248 _pRequestStream = new HTTPOutputStream(*this);
249 request.write(*_pRequestStream);
250 }
251 _lastRequest.update();
252 return *_pRequestStream;
253 }
254 catch (Exception&)
255 {
256 close();
257 throw;
258 }
259}
260
261
262std::istream& HTTPClientSession::receiveResponse(HTTPResponse& response)
263{
264 _pRequestStream = 0;
265 if (networkException()) networkException()->rethrow();
266
267 if (!_responseReceived)
268 {
269 do
270 {
271 response.clear();
272 HTTPHeaderInputStream his(*this);
273 try
274 {
275 response.read(his);
276 }
277 catch (Exception&)
278 {
279 close();
280 if (networkException())
281 networkException()->rethrow();
282 else
283 throw;
284 throw;
285 }
286 }
287 while (response.getStatus() == HTTPResponse::HTTP_CONTINUE);
288 }
289
290 _mustReconnect = getKeepAlive() && !response.getKeepAlive();
291
292 if (!_expectResponseBody || response.getStatus() < 200 || response.getStatus() == HTTPResponse::HTTP_NO_CONTENT || response.getStatus() == HTTPResponse::HTTP_NOT_MODIFIED)
293 _pResponseStream = new HTTPFixedLengthInputStream(*this, 0);
294 else if (response.getChunkedTransferEncoding())
295 _pResponseStream = new HTTPChunkedInputStream(*this);
296 else if (response.hasContentLength())
297#if defined(POCO_HAVE_INT64)
298 _pResponseStream = new HTTPFixedLengthInputStream(*this, response.getContentLength64());
299#else
300 _pResponseStream = new HTTPFixedLengthInputStream(*this, response.getContentLength());
301#endif
302 else
303 _pResponseStream = new HTTPInputStream(*this);
304
305 return *_pResponseStream;
306}
307
308
309bool HTTPClientSession::peekResponse(HTTPResponse& response)
310{
311 poco_assert (!_responseReceived);
312
313 _pRequestStream->flush();
314
315 if (networkException()) networkException()->rethrow();
316
317 response.clear();
318 HTTPHeaderInputStream his(*this);
319 try
320 {
321 response.read(his);
322 }
323 catch (Exception&)
324 {
325 close();
326 if (networkException())
327 networkException()->rethrow();
328 else
329 throw;
330 throw;
331 }
332 _responseReceived = response.getStatus() != HTTPResponse::HTTP_CONTINUE;
333 return !_responseReceived;
334}
335
336
337void HTTPClientSession::reset()
338{
339 close();
340}
341
342
343bool HTTPClientSession::secure() const
344{
345 return false;
346}
347
348
349int HTTPClientSession::write(const char* buffer, std::streamsize length)
350{
351 try
352 {
353 int rc = HTTPSession::write(buffer, length);
354 _reconnect = false;
355 return rc;
356 }
357 catch (Poco::Exception&)
358 {
359 if (_reconnect)
360 {
361 close();
362 reconnect();
363 int rc = HTTPSession::write(buffer, length);
364 clearException();
365 _reconnect = false;
366 return rc;
367 }
368 else throw;
369 }
370}
371
372
373void HTTPClientSession::reconnect()
374{
375 if (_proxyConfig.host.empty() || bypassProxy())
376 {
377 SocketAddress addr(_host, _port);
378 connect(addr);
379 }
380 else
381 {
382 SocketAddress addr(_proxyConfig.host, _proxyConfig.port);
383 connect(addr);
384 }
385}
386
387
388std::string HTTPClientSession::proxyRequestPrefix() const
389{
390 std::string result("http://");
391 result.append(_host);
392 result.append(":");
393 NumberFormatter::append(result, _port);
394 return result;
395}
396
397
398bool HTTPClientSession::mustReconnect() const
399{
400 if (!_mustReconnect)
401 {
402 Poco::Timestamp now;
403 return _keepAliveTimeout <= now - _lastRequest;
404 }
405 else return true;
406}
407
408
409void HTTPClientSession::proxyAuthenticate(HTTPRequest& request)
410{
411 proxyAuthenticateImpl(request);
412}
413
414
415void HTTPClientSession::proxyAuthenticateImpl(HTTPRequest& request)
416{
417 if (!_proxyConfig.username.empty())
418 {
419 HTTPBasicCredentials creds(_proxyConfig.username, _proxyConfig.password);
420 creds.proxyAuthenticate(request);
421 }
422}
423
424
425StreamSocket HTTPClientSession::proxyConnect()
426{
427 ProxyConfig emptyProxyConfig;
428 HTTPClientSession proxySession(getProxyHost(), getProxyPort(), emptyProxyConfig);
429 proxySession.setTimeout(getTimeout());
430 std::string targetAddress(_host);
431 targetAddress.append(":");
432 NumberFormatter::append(targetAddress, _port);
433 HTTPRequest proxyRequest(HTTPRequest::HTTP_CONNECT, targetAddress, HTTPMessage::HTTP_1_1);
434 HTTPResponse proxyResponse;
435 proxyRequest.set("Proxy-Connection", "keep-alive");
436 proxyRequest.set("Host", getHost());
437 proxyAuthenticateImpl(proxyRequest);
438 proxySession.setKeepAlive(true);
439 proxySession.sendRequest(proxyRequest);
440 proxySession.receiveResponse(proxyResponse);
441 if (proxyResponse.getStatus() != HTTPResponse::HTTP_OK)
442 throw HTTPException("Cannot establish proxy connection", proxyResponse.getReason());
443 return proxySession.detachSocket();
444}
445
446
447void HTTPClientSession::proxyTunnel()
448{
449 StreamSocket ss = proxyConnect();
450 attachSocket(ss);
451}
452
453
454bool HTTPClientSession::bypassProxy() const
455{
456 if (!_proxyConfig.nonProxyHosts.empty())
457 {
458 return RegularExpression::match(_host, _proxyConfig.nonProxyHosts, RegularExpression::RE_CASELESS | RegularExpression::RE_ANCHORED);
459 }
460 else return false;
461}
462
463
464} } // namespace Poco::Net
465