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/HTTPCredentials.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 _sourceAddress4(IPAddress::wildcard(IPAddress::IPv4), 0),
44 _sourceAddress6(IPAddress::wildcard(IPAddress::IPv6), 0),
45 _proxyConfig(_globalProxyConfig),
46 _keepAliveTimeout(DEFAULT_KEEP_ALIVE_TIMEOUT, 0),
47 _reconnect(false),
48 _mustReconnect(false),
49 _expectResponseBody(false),
50 _responseReceived(false),
51 _ntlmProxyAuthenticated(false)
52{
53}
54
55
56HTTPClientSession::HTTPClientSession(const StreamSocket& socket):
57 HTTPSession(socket),
58 _port(HTTPSession::HTTP_PORT),
59 _sourceAddress4(IPAddress::wildcard(IPAddress::IPv4), 0),
60 _sourceAddress6(IPAddress::wildcard(IPAddress::IPv6), 0),
61 _proxyConfig(_globalProxyConfig),
62 _keepAliveTimeout(DEFAULT_KEEP_ALIVE_TIMEOUT, 0),
63 _reconnect(false),
64 _mustReconnect(false),
65 _expectResponseBody(false),
66 _responseReceived(false),
67 _ntlmProxyAuthenticated(false)
68{
69 setKeepAlive(true);
70}
71
72
73HTTPClientSession::HTTPClientSession(const SocketAddress& address):
74 _host(address.host().toString()),
75 _port(address.port()),
76 _sourceAddress4(IPAddress::wildcard(IPAddress::IPv4), 0),
77 _sourceAddress6(IPAddress::wildcard(IPAddress::IPv6), 0),
78 _proxyConfig(_globalProxyConfig),
79 _keepAliveTimeout(DEFAULT_KEEP_ALIVE_TIMEOUT, 0),
80 _reconnect(false),
81 _mustReconnect(false),
82 _expectResponseBody(false),
83 _responseReceived(false),
84 _ntlmProxyAuthenticated(false)
85{
86}
87
88
89HTTPClientSession::HTTPClientSession(const std::string& host, Poco::UInt16 port):
90 _host(host),
91 _port(port),
92 _sourceAddress4(IPAddress::wildcard(IPAddress::IPv4), 0),
93 _sourceAddress6(IPAddress::wildcard(IPAddress::IPv6), 0),
94 _proxyConfig(_globalProxyConfig),
95 _keepAliveTimeout(DEFAULT_KEEP_ALIVE_TIMEOUT, 0),
96 _reconnect(false),
97 _mustReconnect(false),
98 _expectResponseBody(false),
99 _responseReceived(false),
100 _ntlmProxyAuthenticated(false)
101{
102}
103
104
105HTTPClientSession::HTTPClientSession(const std::string& host, Poco::UInt16 port, const ProxyConfig& proxyConfig):
106 _host(host),
107 _port(port),
108 _sourceAddress4(IPAddress::wildcard(IPAddress::IPv4), 0),
109 _sourceAddress6(IPAddress::wildcard(IPAddress::IPv6), 0),
110 _proxyConfig(proxyConfig),
111 _keepAliveTimeout(DEFAULT_KEEP_ALIVE_TIMEOUT, 0),
112 _reconnect(false),
113 _mustReconnect(false),
114 _expectResponseBody(false),
115 _responseReceived(false)
116{
117}
118
119
120HTTPClientSession::HTTPClientSession(const StreamSocket& socket, const ProxyConfig& proxyConfig):
121 HTTPSession(socket),
122 _port(HTTPSession::HTTP_PORT),
123 _sourceAddress4(IPAddress::wildcard(IPAddress::IPv4), 0),
124 _sourceAddress6(IPAddress::wildcard(IPAddress::IPv6), 0),
125 _proxyConfig(proxyConfig),
126 _keepAliveTimeout(DEFAULT_KEEP_ALIVE_TIMEOUT, 0),
127 _reconnect(false),
128 _mustReconnect(false),
129 _expectResponseBody(false),
130 _responseReceived(false)
131{
132 setKeepAlive(true);
133}
134
135
136HTTPClientSession::~HTTPClientSession()
137{
138}
139
140
141void HTTPClientSession::setHost(const std::string& host)
142{
143 if (!connected())
144 _host = host;
145 else
146 throw IllegalStateException("Cannot set the host for an already connected session");
147}
148
149
150void HTTPClientSession::setPort(Poco::UInt16 port)
151{
152 if (!connected())
153 _port = port;
154 else
155 throw IllegalStateException("Cannot set the port number for an already connected session");
156}
157
158
159void HTTPClientSession::setSourceAddress(const SocketAddress& address)
160{
161 if (!connected())
162 {
163 if (address.family() == IPAddress::IPv4)
164 _sourceAddress4 = address;
165 else
166 _sourceAddress6 = address;
167 _sourceAddress = address;
168 }
169 else
170 throw IllegalStateException("Cannot set the source address for an already connected session");
171}
172
173
174const SocketAddress& HTTPClientSession::getSourceAddress()
175{
176 return _sourceAddress;
177}
178
179
180const SocketAddress& HTTPClientSession::getSourceAddress4()
181{
182 return _sourceAddress4;
183}
184
185
186const SocketAddress& HTTPClientSession::getSourceAddress6()
187{
188 return _sourceAddress6;
189}
190
191
192void HTTPClientSession::setProxy(const std::string& host, Poco::UInt16 port)
193{
194 if (!connected())
195 {
196 _proxyConfig.host = host;
197 _proxyConfig.port = port;
198 }
199 else throw IllegalStateException("Cannot set the proxy host and port for an already connected session");
200}
201
202
203void HTTPClientSession::setProxyHost(const std::string& host)
204{
205 if (!connected())
206 _proxyConfig.host = host;
207 else
208 throw IllegalStateException("Cannot set the proxy host for an already connected session");
209}
210
211
212void HTTPClientSession::setProxyPort(Poco::UInt16 port)
213{
214 if (!connected())
215 _proxyConfig.port = port;
216 else
217 throw IllegalStateException("Cannot set the proxy port number for an already connected session");
218}
219
220
221void HTTPClientSession::setProxyCredentials(const std::string& username, const std::string& password)
222{
223 _proxyConfig.username = username;
224 _proxyConfig.password = password;
225}
226
227
228void HTTPClientSession::setProxyUsername(const std::string& username)
229{
230 _proxyConfig.username = username;
231}
232
233
234void HTTPClientSession::setProxyPassword(const std::string& password)
235{
236 _proxyConfig.password = password;
237}
238
239
240void HTTPClientSession::setProxyConfig(const ProxyConfig& config)
241{
242 _proxyConfig = config;
243}
244
245
246void HTTPClientSession::setGlobalProxyConfig(const ProxyConfig& config)
247{
248 _globalProxyConfig = config;
249}
250
251
252void HTTPClientSession::setKeepAliveTimeout(const Poco::Timespan& timeout)
253{
254 _keepAliveTimeout = timeout;
255}
256
257
258std::ostream& HTTPClientSession::sendRequest(HTTPRequest& request)
259{
260 _pRequestStream = 0;
261 _pResponseStream = 0;
262
263 bool keepAlive = getKeepAlive();
264 if (((connected() && !keepAlive) || mustReconnect()) && !_host.empty())
265 {
266 close();
267 _mustReconnect = false;
268 }
269 try
270 {
271 if (!connected())
272 {
273 _ntlmProxyAuthenticated = false;
274 reconnect();
275 }
276 if (!keepAlive)
277 {
278 request.setKeepAlive(false);
279 }
280 if (!request.has(HTTPRequest::HOST) && !_host.empty())
281 {
282 request.setHost(_host, _port);
283 }
284 if (!_proxyConfig.host.empty() && !bypassProxy())
285 {
286 std::string prefix = proxyRequestPrefix();
287 if (!prefix.empty() && request.getURI().compare(0, 7, "http://") != 0 && request.getURI().compare(0, 8, "https://") != 0)
288 request.setURI(prefix + request.getURI());
289 if (keepAlive) request.set(HTTPMessage::PROXY_CONNECTION, HTTPMessage::CONNECTION_KEEP_ALIVE);
290 proxyAuthenticate(request);
291 }
292 _reconnect = keepAlive;
293 return sendRequestImpl(request);
294 }
295 catch (Exception&)
296 {
297 close();
298 throw;
299 }
300}
301
302
303std::ostream& HTTPClientSession::sendRequestImpl(const HTTPRequest& request)
304{
305 _pRequestStream = 0;
306 _pResponseStream = 0;
307 clearException();
308 _responseReceived = false;
309 _expectResponseBody = request.getMethod() != HTTPRequest::HTTP_HEAD;
310 const std::string& method = request.getMethod();
311 if (request.getChunkedTransferEncoding())
312 {
313 HTTPHeaderOutputStream hos(*this);
314 request.write(hos);
315 _pRequestStream = new HTTPChunkedOutputStream(*this);
316 }
317 else if (request.hasContentLength())
318 {
319 Poco::CountingOutputStream cs;
320 request.write(cs);
321#if POCO_HAVE_INT64
322 _pRequestStream = new HTTPFixedLengthOutputStream(*this, request.getContentLength64() + cs.chars());
323#else
324 _pRequestStream = new HTTPFixedLengthOutputStream(*this, request.getContentLength() + cs.chars());
325#endif
326 request.write(*_pRequestStream);
327 }
328 else if ((method != HTTPRequest::HTTP_PUT && method != HTTPRequest::HTTP_POST && method != HTTPRequest::HTTP_PATCH) || request.has(HTTPRequest::UPGRADE))
329 {
330 Poco::CountingOutputStream cs;
331 request.write(cs);
332 _pRequestStream = new HTTPFixedLengthOutputStream(*this, cs.chars());
333 request.write(*_pRequestStream);
334 }
335 else
336 {
337 _pRequestStream = new HTTPOutputStream(*this);
338 request.write(*_pRequestStream);
339 }
340 _lastRequest.update();
341 return *_pRequestStream;
342 }
343
344
345void HTTPClientSession::flushRequest()
346{
347 _pRequestStream = 0;
348 if (networkException()) networkException()->rethrow();
349}
350
351
352std::istream& HTTPClientSession::receiveResponse(HTTPResponse& response)
353{
354 flushRequest();
355 if (!_responseReceived)
356 {
357 do
358 {
359 response.clear();
360 HTTPHeaderInputStream his(*this);
361 try
362 {
363 response.read(his);
364 }
365 catch (Exception&)
366 {
367 close();
368 if (networkException())
369 networkException()->rethrow();
370 else
371 throw;
372 throw;
373 }
374 }
375 while (response.getStatus() == HTTPResponse::HTTP_CONTINUE);
376 }
377
378 _mustReconnect = getKeepAlive() && !response.getKeepAlive();
379
380 if (!_expectResponseBody || response.getStatus() < 200 || response.getStatus() == HTTPResponse::HTTP_NO_CONTENT || response.getStatus() == HTTPResponse::HTTP_NOT_MODIFIED)
381 _pResponseStream = new HTTPFixedLengthInputStream(*this, 0);
382 else if (response.getChunkedTransferEncoding())
383 _pResponseStream = new HTTPChunkedInputStream(*this);
384 else if (response.hasContentLength())
385#if defined(POCO_HAVE_INT64)
386 _pResponseStream = new HTTPFixedLengthInputStream(*this, response.getContentLength64());
387#else
388 _pResponseStream = new HTTPFixedLengthInputStream(*this, response.getContentLength());
389#endif
390 else
391 _pResponseStream = new HTTPInputStream(*this);
392
393 return *_pResponseStream;
394}
395
396
397bool HTTPClientSession::peekResponse(HTTPResponse& response)
398{
399 poco_assert (!_responseReceived);
400
401 _pRequestStream->flush();
402
403 if (networkException()) networkException()->rethrow();
404
405 response.clear();
406 HTTPHeaderInputStream his(*this);
407 try
408 {
409 response.read(his);
410 }
411 catch (Exception&)
412 {
413 close();
414 if (networkException())
415 networkException()->rethrow();
416 else
417 throw;
418 throw;
419 }
420 _responseReceived = response.getStatus() != HTTPResponse::HTTP_CONTINUE;
421 return !_responseReceived;
422}
423
424
425void HTTPClientSession::reset()
426{
427 close();
428}
429
430
431bool HTTPClientSession::secure() const
432{
433 return false;
434}
435
436
437int HTTPClientSession::write(const char* buffer, std::streamsize length)
438{
439 try
440 {
441 int rc = HTTPSession::write(buffer, length);
442 _reconnect = false;
443 return rc;
444 }
445 catch (IOException&)
446 {
447 if (_reconnect)
448 {
449 close();
450 reconnect();
451 int rc = HTTPSession::write(buffer, length);
452 clearException();
453 _reconnect = false;
454 return rc;
455 }
456 else throw;
457 }
458}
459
460
461void HTTPClientSession::reconnect()
462{
463 SocketAddress addr;
464 if (_proxyConfig.host.empty() || bypassProxy())
465 addr = SocketAddress(_host, _port);
466 else
467 addr = SocketAddress(_proxyConfig.host, _proxyConfig.port);
468
469 SocketAddress sourceAddr;
470 if (addr.family() == IPAddress::IPv4)
471 sourceAddr = _sourceAddress4;
472 else
473 sourceAddr = _sourceAddress6;
474
475 if ((!sourceAddr.host().isWildcard()) || (sourceAddr.port() != 0))
476 connect(addr, sourceAddr);
477 else
478 connect(addr);
479}
480
481
482std::string HTTPClientSession::proxyRequestPrefix() const
483{
484 std::string result("http://");
485 result.append(_host);
486 result.append(":");
487 NumberFormatter::append(result, _port);
488 return result;
489}
490
491
492bool HTTPClientSession::mustReconnect() const
493{
494 if (!_mustReconnect)
495 {
496 Poco::Timestamp now;
497 return _keepAliveTimeout <= now - _lastRequest;
498 }
499 else return true;
500}
501
502
503void HTTPClientSession::proxyAuthenticate(HTTPRequest& request)
504{
505 proxyAuthenticateImpl(request, _proxyConfig);
506}
507
508
509void HTTPClientSession::proxyAuthenticateImpl(HTTPRequest& request, const ProxyConfig& proxyConfig)
510{
511 switch (proxyConfig.authMethod)
512 {
513 case PROXY_AUTH_NONE:
514 break;
515
516 case PROXY_AUTH_HTTP_BASIC:
517 _proxyBasicCreds.setUsername(proxyConfig.username);
518 _proxyBasicCreds.setPassword(proxyConfig.password);
519 _proxyBasicCreds.proxyAuthenticate(request);
520 break;
521
522 case PROXY_AUTH_HTTP_DIGEST:
523 if (HTTPCredentials::hasDigestCredentials(request))
524 {
525 _proxyDigestCreds.updateProxyAuthInfo(request);
526 }
527 else
528 {
529 _proxyDigestCreds.setUsername(proxyConfig.username);
530 _proxyDigestCreds.setPassword(proxyConfig.password);
531 proxyAuthenticateDigest(request);
532 }
533
534 case PROXY_AUTH_NTLM:
535 if (_ntlmProxyAuthenticated)
536 {
537 _proxyNTLMCreds.updateProxyAuthInfo(request);
538 }
539 else
540 {
541 _proxyNTLMCreds.setUsername(proxyConfig.username);
542 _proxyNTLMCreds.setPassword(proxyConfig.password);
543 _proxyNTLMCreds.setHost(proxyConfig.host);
544 proxyAuthenticateNTLM(request);
545 _ntlmProxyAuthenticated = true;
546 }
547 }
548}
549
550
551void HTTPClientSession::proxyAuthenticateDigest(HTTPRequest& request)
552{
553 HTTPResponse response;
554 request.set(HTTPMessage::PROXY_CONNECTION, HTTPMessage::CONNECTION_KEEP_ALIVE);
555 sendChallengeRequest(request, response);
556 _proxyDigestCreds.proxyAuthenticate(request, response);
557}
558
559
560void HTTPClientSession::proxyAuthenticateNTLM(HTTPRequest& request)
561{
562 HTTPResponse response;
563 request.set(HTTPMessage::PROXY_CONNECTION, HTTPMessage::CONNECTION_KEEP_ALIVE);
564 _proxyNTLMCreds.proxyAuthenticate(request, std::string());
565 sendChallengeRequest(request, response);
566 _proxyNTLMCreds.proxyAuthenticate(request, response);
567}
568
569
570void HTTPClientSession::sendChallengeRequest(const HTTPRequest& request, HTTPResponse& response)
571 {
572 if (!connected())
573 {
574 reconnect();
575 }
576
577 HTTPRequest challengeRequest(request);
578 if (challengeRequest.hasContentLength())
579 {
580 challengeRequest.setContentLength(0);
581 }
582
583 sendRequestImpl(challengeRequest);
584 std::istream& istr = receiveResponse(response);
585 while (istr.good()) istr.get();
586}
587
588
589StreamSocket HTTPClientSession::proxyConnect()
590{
591 ProxyConfig emptyProxyConfig;
592 HTTPClientSession proxySession(getProxyHost(), getProxyPort(), emptyProxyConfig);
593 proxySession.setTimeout(getTimeout());
594 std::string targetAddress(_host);
595 targetAddress.append(":");
596 NumberFormatter::append(targetAddress, _port);
597 HTTPRequest proxyRequest(HTTPRequest::HTTP_CONNECT, targetAddress, HTTPMessage::HTTP_1_1);
598 HTTPResponse proxyResponse;
599 proxyRequest.set(HTTPMessage::PROXY_CONNECTION, HTTPMessage::CONNECTION_KEEP_ALIVE);
600 proxyRequest.set(HTTPRequest::HOST, getHost());
601 proxySession.proxyAuthenticateImpl(proxyRequest, _proxyConfig);
602 proxySession.setKeepAlive(true);
603 proxySession.setSourceAddress(_sourceAddress4);
604 proxySession.setSourceAddress(_sourceAddress6);
605 proxySession.sendRequest(proxyRequest);
606 proxySession.receiveResponse(proxyResponse);
607 if (proxyResponse.getStatus() != HTTPResponse::HTTP_OK)
608 throw HTTPException("Cannot establish proxy connection", proxyResponse.getReason());
609 return proxySession.detachSocket();
610}
611
612
613void HTTPClientSession::proxyTunnel()
614{
615 StreamSocket ss = proxyConnect();
616 attachSocket(ss);
617}
618
619
620bool HTTPClientSession::bypassProxy() const
621{
622 if (!_proxyConfig.nonProxyHosts.empty())
623 {
624 return RegularExpression::match(_host, _proxyConfig.nonProxyHosts, RegularExpression::RE_CASELESS | RegularExpression::RE_ANCHORED);
625 }
626 else return false;
627}
628
629
630} } // namespace Poco::Net
631