1 | // |
2 | // HTTPSClientSessionTest.cpp |
3 | // |
4 | // Copyright (c) 2006, Applied Informatics Software Engineering GmbH. |
5 | // and Contributors. |
6 | // |
7 | // SPDX-License-Identifier: BSL-1.0 |
8 | // |
9 | |
10 | |
11 | #include "HTTPSClientSessionTest.h" |
12 | #include "Poco/CppUnit/TestCaller.h" |
13 | #include "Poco/CppUnit/TestSuite.h" |
14 | #include "Poco/Net/HTTPSClientSession.h" |
15 | #include "Poco/Net/HTTPRequest.h" |
16 | #include "Poco/Net/HTTPRequestHandler.h" |
17 | #include "Poco/Net/HTTPRequestHandlerFactory.h" |
18 | #include "Poco/Net/HTTPResponse.h" |
19 | #include "Poco/Net/HTTPServer.h" |
20 | #include "Poco/Net/HTTPServerResponse.h" |
21 | #include "Poco/Net/HTTPServerRequest.h" |
22 | #include "Poco/Net/HTTPServerParams.h" |
23 | #include "Poco/Net/SecureStreamSocket.h" |
24 | #include "Poco/Net/Context.h" |
25 | #include "Poco/Net/Session.h" |
26 | #include "Poco/Net/SSLManager.h" |
27 | #include "Poco/Net/SSLException.h" |
28 | #include "Poco/Util/Application.h" |
29 | #include "Poco/Util/AbstractConfiguration.h" |
30 | #include "Poco/StreamCopier.h" |
31 | #include "Poco/Exception.h" |
32 | #include "Poco/DateTimeFormatter.h" |
33 | #include "Poco/DateTimeFormat.h" |
34 | #include "Poco/Thread.h" |
35 | #include "HTTPSTestServer.h" |
36 | #include <istream> |
37 | #include <ostream> |
38 | #include <sstream> |
39 | #include <iostream> |
40 | |
41 | |
42 | using namespace Poco::Net; |
43 | using Poco::Util::Application; |
44 | using Poco::StreamCopier; |
45 | using Poco::Thread; |
46 | |
47 | |
48 | class TestRequestHandler: public HTTPRequestHandler |
49 | /// Return a HTML document with the current date and time. |
50 | { |
51 | public: |
52 | TestRequestHandler() |
53 | { |
54 | } |
55 | |
56 | void handleRequest(HTTPServerRequest& request, HTTPServerResponse& response) |
57 | { |
58 | response.setChunkedTransferEncoding(true); |
59 | response.setContentType(request.getContentType()); |
60 | std::ostream& ostr = response.send(); |
61 | Poco::StreamCopier::copyStream(request.stream(), ostr); |
62 | } |
63 | |
64 | }; |
65 | |
66 | |
67 | class TestRequestHandlerFactory: public HTTPRequestHandlerFactory |
68 | { |
69 | public: |
70 | TestRequestHandlerFactory() |
71 | { |
72 | } |
73 | |
74 | HTTPRequestHandler* createRequestHandler(const HTTPServerRequest& request) |
75 | { |
76 | return new TestRequestHandler(); |
77 | } |
78 | }; |
79 | |
80 | |
81 | HTTPSClientSessionTest::HTTPSClientSessionTest(const std::string& name): CppUnit::TestCase(name) |
82 | { |
83 | } |
84 | |
85 | |
86 | HTTPSClientSessionTest::~HTTPSClientSessionTest() |
87 | { |
88 | } |
89 | |
90 | |
91 | void HTTPSClientSessionTest::testGetSmall() |
92 | { |
93 | HTTPSTestServer srv; |
94 | HTTPSClientSession s("127.0.0.1" , srv.port()); |
95 | HTTPRequest request(HTTPRequest::HTTP_GET, "/small" ); |
96 | s.sendRequest(request); |
97 | HTTPResponse response; |
98 | std::istream& rs = s.receiveResponse(response); |
99 | assertTrue (response.getContentLength() == HTTPSTestServer::SMALL_BODY.length()); |
100 | assertTrue (response.getContentType() == "text/plain" ); |
101 | std::ostringstream ostr; |
102 | StreamCopier::copyStream(rs, ostr); |
103 | assertTrue (ostr.str() == HTTPSTestServer::SMALL_BODY); |
104 | } |
105 | |
106 | |
107 | void HTTPSClientSessionTest::testGetLarge() |
108 | { |
109 | HTTPSTestServer srv; |
110 | HTTPSClientSession s("127.0.0.1" , srv.port()); |
111 | HTTPRequest request(HTTPRequest::HTTP_GET, "/large" ); |
112 | s.sendRequest(request); |
113 | HTTPResponse response; |
114 | std::istream& rs = s.receiveResponse(response); |
115 | assertTrue (response.getContentLength() == HTTPSTestServer::LARGE_BODY.length()); |
116 | assertTrue (response.getContentType() == "text/plain" ); |
117 | std::ostringstream ostr; |
118 | StreamCopier::copyStream(rs, ostr); |
119 | assertTrue (ostr.str() == HTTPSTestServer::LARGE_BODY); |
120 | } |
121 | |
122 | |
123 | void HTTPSClientSessionTest::testHead() |
124 | { |
125 | HTTPSTestServer srv; |
126 | HTTPSClientSession s("127.0.0.1" , srv.port()); |
127 | HTTPRequest request(HTTPRequest::HTTP_HEAD, "/large" ); |
128 | s.sendRequest(request); |
129 | HTTPResponse response; |
130 | std::istream& rs = s.receiveResponse(response); |
131 | assertTrue (response.getContentLength() == HTTPSTestServer::LARGE_BODY.length()); |
132 | assertTrue (response.getContentType() == "text/plain" ); |
133 | std::ostringstream ostr; |
134 | assertTrue (StreamCopier::copyStream(rs, ostr) == 0); |
135 | } |
136 | |
137 | |
138 | void HTTPSClientSessionTest::testPostSmallIdentity() |
139 | { |
140 | HTTPSTestServer srv; |
141 | HTTPSClientSession s("127.0.0.1" , srv.port()); |
142 | HTTPRequest request(HTTPRequest::HTTP_POST, "/echo" ); |
143 | std::string body("this is a random request body\r\n0\r\n" ); |
144 | request.setContentLength((int) body.length()); |
145 | s.sendRequest(request) << body; |
146 | HTTPResponse response; |
147 | std::istream& rs = s.receiveResponse(response); |
148 | assertTrue (response.getContentLength() == body.length()); |
149 | std::ostringstream ostr; |
150 | StreamCopier::copyStream(rs, ostr); |
151 | assertTrue (ostr.str() == body); |
152 | } |
153 | |
154 | |
155 | void HTTPSClientSessionTest::testPostLargeIdentity() |
156 | { |
157 | HTTPSTestServer srv; |
158 | HTTPSClientSession s("127.0.0.1" , srv.port()); |
159 | HTTPRequest request(HTTPRequest::HTTP_POST, "/echo" ); |
160 | std::string body(8000, 'x'); |
161 | body.append("\r\n0\r\n" ); |
162 | request.setContentLength((int) body.length()); |
163 | s.sendRequest(request) << body; |
164 | HTTPResponse response; |
165 | std::istream& rs = s.receiveResponse(response); |
166 | assertTrue (response.getContentLength() == body.length()); |
167 | std::ostringstream ostr; |
168 | StreamCopier::copyStream(rs, ostr); |
169 | assertTrue (ostr.str() == body); |
170 | } |
171 | |
172 | |
173 | void HTTPSClientSessionTest::testPostSmallChunked() |
174 | { |
175 | HTTPSTestServer srv; |
176 | HTTPSClientSession s("127.0.0.1" , srv.port()); |
177 | HTTPRequest request(HTTPRequest::HTTP_POST, "/echo" ); |
178 | std::string body("this is a random request body" ); |
179 | request.setChunkedTransferEncoding(true); |
180 | s.sendRequest(request) << body; |
181 | HTTPResponse response; |
182 | std::istream& rs = s.receiveResponse(response); |
183 | assertTrue (response.getChunkedTransferEncoding()); |
184 | assertTrue (response.getContentLength() == HTTPMessage::UNKNOWN_CONTENT_LENGTH); |
185 | std::ostringstream ostr; |
186 | StreamCopier::copyStream(rs, ostr); |
187 | assertTrue (ostr.str() == body); |
188 | } |
189 | |
190 | |
191 | void HTTPSClientSessionTest::testPostLargeChunked() |
192 | { |
193 | HTTPSTestServer srv; |
194 | HTTPSClientSession s("127.0.0.1" , srv.port()); |
195 | HTTPRequest request(HTTPRequest::HTTP_POST, "/echo" ); |
196 | std::string body(16000, 'x'); |
197 | request.setChunkedTransferEncoding(true); |
198 | s.sendRequest(request) << body; |
199 | HTTPResponse response; |
200 | std::istream& rs = s.receiveResponse(response); |
201 | assertTrue (response.getChunkedTransferEncoding()); |
202 | assertTrue (response.getContentLength() == HTTPMessage::UNKNOWN_CONTENT_LENGTH); |
203 | std::ostringstream ostr; |
204 | StreamCopier::copyStream(rs, ostr); |
205 | assertTrue (ostr.str() == body); |
206 | } |
207 | |
208 | |
209 | void HTTPSClientSessionTest::testPostLargeChunkedKeepAlive() |
210 | { |
211 | SecureServerSocket svs(32322); |
212 | HTTPServer srv(new TestRequestHandlerFactory(), svs, new HTTPServerParams()); |
213 | srv.start(); |
214 | try |
215 | { |
216 | HTTPSClientSession s("127.0.0.1" , srv.port()); |
217 | s.setKeepAlive(true); |
218 | for (int i = 0; i < 10; ++i) |
219 | { |
220 | HTTPRequest request(HTTPRequest::HTTP_POST, "/keepAlive" , HTTPMessage::HTTP_1_1); |
221 | std::string body(16000, 'x'); |
222 | request.setChunkedTransferEncoding(true); |
223 | s.sendRequest(request) << body; |
224 | HTTPResponse response; |
225 | std::istream& rs = s.receiveResponse(response); |
226 | assertTrue (response.getChunkedTransferEncoding()); |
227 | assertTrue (response.getContentLength() == HTTPMessage::UNKNOWN_CONTENT_LENGTH); |
228 | std::ostringstream ostr; |
229 | StreamCopier::copyStream(rs, ostr); |
230 | assertTrue (ostr.str() == body); |
231 | } |
232 | srv.stop(); |
233 | } |
234 | catch (...) |
235 | { |
236 | srv.stop(); |
237 | throw; |
238 | } |
239 | } |
240 | |
241 | |
242 | void HTTPSClientSessionTest::testKeepAlive() |
243 | { |
244 | HTTPSTestServer srv; |
245 | HTTPSClientSession s("127.0.0.1" , srv.port()); |
246 | s.setKeepAlive(true); |
247 | HTTPRequest request(HTTPRequest::HTTP_HEAD, "/keepAlive" , HTTPMessage::HTTP_1_1); |
248 | s.sendRequest(request); |
249 | HTTPResponse response; |
250 | std::istream& rs1 = s.receiveResponse(response); |
251 | assertTrue (response.getContentLength() == HTTPSTestServer::SMALL_BODY.length()); |
252 | assertTrue (response.getContentType() == "text/plain" ); |
253 | assertTrue (response.getKeepAlive()); |
254 | std::ostringstream ostr1; |
255 | assertTrue (StreamCopier::copyStream(rs1, ostr1) == 0); |
256 | |
257 | request.setMethod(HTTPRequest::HTTP_GET); |
258 | request.setURI("/small" ); |
259 | s.sendRequest(request); |
260 | std::istream& rs2 = s.receiveResponse(response); |
261 | assertTrue (response.getContentLength() == HTTPSTestServer::SMALL_BODY.length()); |
262 | assertTrue (response.getKeepAlive()); |
263 | std::ostringstream ostr2; |
264 | StreamCopier::copyStream(rs2, ostr2); |
265 | assertTrue (ostr2.str() == HTTPSTestServer::SMALL_BODY); |
266 | |
267 | request.setMethod(HTTPRequest::HTTP_GET); |
268 | request.setURI("/large" ); |
269 | s.sendRequest(request); |
270 | std::istream& rs3 = s.receiveResponse(response); |
271 | assertTrue (response.getContentLength() == HTTPMessage::UNKNOWN_CONTENT_LENGTH); |
272 | assertTrue (response.getChunkedTransferEncoding()); |
273 | assertTrue (response.getKeepAlive()); |
274 | std::ostringstream ostr3; |
275 | StreamCopier::copyStream(rs3, ostr3); |
276 | assertTrue (ostr3.str() == HTTPSTestServer::LARGE_BODY); |
277 | |
278 | request.setMethod(HTTPRequest::HTTP_HEAD); |
279 | request.setURI("/large" ); |
280 | s.sendRequest(request); |
281 | std::istream& rs4 = s.receiveResponse(response); |
282 | assertTrue (response.getContentLength() == HTTPSTestServer::LARGE_BODY.length()); |
283 | assertTrue (response.getContentType() == "text/plain" ); |
284 | assertTrue (!response.getKeepAlive()); |
285 | std::ostringstream ostr4; |
286 | assertTrue (StreamCopier::copyStream(rs4, ostr4) == 0); |
287 | } |
288 | |
289 | |
290 | void HTTPSClientSessionTest::testInterop() |
291 | { |
292 | HTTPSClientSession s("secure.appinf.com" ); |
293 | HTTPRequest request(HTTPRequest::HTTP_GET, "/public/poco/NetSSL.txt" ); |
294 | s.sendRequest(request); |
295 | X509Certificate cert = s.serverCertificate(); |
296 | HTTPResponse response; |
297 | std::istream& rs = s.receiveResponse(response); |
298 | std::ostringstream ostr; |
299 | StreamCopier::copyStream(rs, ostr); |
300 | std::string str(ostr.str()); |
301 | assertTrue (str == "This is a test file for NetSSL.\n" ); |
302 | assertTrue (cert.commonName() == "secure.appinf.com" || cert.commonName() == "*.appinf.com" ); |
303 | } |
304 | |
305 | |
306 | void HTTPSClientSessionTest::testProxy() |
307 | { |
308 | HTTPSTestServer srv; |
309 | HTTPSClientSession s("secure.appinf.com" ); |
310 | s.setProxy( |
311 | Application::instance().config().getString("testsuite.proxy.host" ), |
312 | Application::instance().config().getInt("testsuite.proxy.port" ) |
313 | ); |
314 | HTTPRequest request(HTTPRequest::HTTP_GET, "/public/poco/NetSSL.txt" ); |
315 | s.sendRequest(request); |
316 | X509Certificate cert = s.serverCertificate(); |
317 | HTTPResponse response; |
318 | std::istream& rs = s.receiveResponse(response); |
319 | std::ostringstream ostr; |
320 | StreamCopier::copyStream(rs, ostr); |
321 | std::string str(ostr.str()); |
322 | assertTrue (str == "This is a test file for NetSSL.\n" ); |
323 | assertTrue (cert.commonName() == "secure.appinf.com" || cert.commonName() == "*.appinf.com" ); |
324 | } |
325 | |
326 | |
327 | void HTTPSClientSessionTest::testCachedSession() |
328 | { |
329 | // ensure OpenSSL machinery is fully setup |
330 | Context::Ptr pDefaultServerContext = SSLManager::instance().defaultServerContext(); |
331 | Context::Ptr pDefaultClientContext = SSLManager::instance().defaultClientContext(); |
332 | |
333 | Context::Ptr pServerContext = new Context( |
334 | Context::SERVER_USE, |
335 | Application::instance().config().getString("openSSL.server.privateKeyFile" ), |
336 | Application::instance().config().getString("openSSL.server.privateKeyFile" ), |
337 | Application::instance().config().getString("openSSL.server.caConfig" ), |
338 | Context::VERIFY_NONE, |
339 | 9, |
340 | true, |
341 | "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH" ); |
342 | pServerContext->enableSessionCache(true, "TestSuite" ); |
343 | pServerContext->setSessionTimeout(10); |
344 | pServerContext->setSessionCacheSize(1000); |
345 | pServerContext->disableStatelessSessionResumption(); |
346 | |
347 | HTTPSTestServer srv(pServerContext); |
348 | |
349 | Context::Ptr pClientContext = new Context( |
350 | Context::CLIENT_USE, |
351 | Application::instance().config().getString("openSSL.client.privateKeyFile" ), |
352 | Application::instance().config().getString("openSSL.client.privateKeyFile" ), |
353 | Application::instance().config().getString("openSSL.client.caConfig" ), |
354 | Context::VERIFY_RELAXED, |
355 | 9, |
356 | true, |
357 | "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH" ); |
358 | pClientContext->enableSessionCache(true); |
359 | |
360 | HTTPSClientSession s1("127.0.0.1" , srv.port(), pClientContext); |
361 | HTTPRequest request1(HTTPRequest::HTTP_GET, "/small" ); |
362 | s1.sendRequest(request1); |
363 | Session::Ptr pSession1 = s1.sslSession(); |
364 | HTTPResponse response1; |
365 | std::istream& rs1 = s1.receiveResponse(response1); |
366 | assertTrue (response1.getContentLength() == HTTPSTestServer::SMALL_BODY.length()); |
367 | assertTrue (response1.getContentType() == "text/plain" ); |
368 | std::ostringstream ostr1; |
369 | StreamCopier::copyStream(rs1, ostr1); |
370 | assertTrue (ostr1.str() == HTTPSTestServer::SMALL_BODY); |
371 | |
372 | HTTPSClientSession s2("127.0.0.1" , srv.port(), pClientContext, pSession1); |
373 | HTTPRequest request2(HTTPRequest::HTTP_GET, "/small" ); |
374 | s2.sendRequest(request2); |
375 | Session::Ptr pSession2 = s2.sslSession(); |
376 | HTTPResponse response2; |
377 | std::istream& rs2 = s2.receiveResponse(response2); |
378 | assertTrue (response2.getContentLength() == HTTPSTestServer::SMALL_BODY.length()); |
379 | assertTrue (response2.getContentType() == "text/plain" ); |
380 | std::ostringstream ostr2; |
381 | StreamCopier::copyStream(rs2, ostr2); |
382 | assertTrue (ostr2.str() == HTTPSTestServer::SMALL_BODY); |
383 | |
384 | assertTrue (pSession1 == pSession2); |
385 | |
386 | HTTPRequest request3(HTTPRequest::HTTP_GET, "/small" ); |
387 | s2.sendRequest(request3); |
388 | Session::Ptr pSession3 = s2.sslSession(); |
389 | HTTPResponse response3; |
390 | std::istream& rs3 = s2.receiveResponse(response3); |
391 | assertTrue (response3.getContentLength() == HTTPSTestServer::SMALL_BODY.length()); |
392 | assertTrue (response3.getContentType() == "text/plain" ); |
393 | std::ostringstream ostr3; |
394 | StreamCopier::copyStream(rs3, ostr3); |
395 | assertTrue (ostr3.str() == HTTPSTestServer::SMALL_BODY); |
396 | |
397 | assertTrue (pSession1 == pSession3); |
398 | |
399 | Thread::sleep(15000); // wait for session to expire |
400 | pServerContext->flushSessionCache(); |
401 | |
402 | HTTPRequest request4(HTTPRequest::HTTP_GET, "/small" ); |
403 | s2.sendRequest(request4); |
404 | Session::Ptr pSession4 = s2.sslSession(); |
405 | HTTPResponse response4; |
406 | std::istream& rs4 = s2.receiveResponse(response4); |
407 | assertTrue (response4.getContentLength() == HTTPSTestServer::SMALL_BODY.length()); |
408 | assertTrue (response4.getContentType() == "text/plain" ); |
409 | std::ostringstream ostr4; |
410 | StreamCopier::copyStream(rs4, ostr4); |
411 | assertTrue (ostr4.str() == HTTPSTestServer::SMALL_BODY); |
412 | |
413 | assertTrue (pSession1 != pSession4); |
414 | } |
415 | |
416 | |
417 | void HTTPSClientSessionTest::testUnknownContentLength() |
418 | { |
419 | HTTPSTestServer srv; |
420 | HTTPSClientSession s("127.0.0.1" , srv.port()); |
421 | HTTPRequest request(HTTPRequest::HTTP_GET, "/nolength" ); |
422 | s.sendRequest(request); |
423 | HTTPResponse response; |
424 | std::istream& rs = s.receiveResponse(response); |
425 | assertTrue (response.getContentLength() == HTTPMessage::UNKNOWN_CONTENT_LENGTH); |
426 | assertTrue (response.getContentType() == "text/plain" ); |
427 | std::ostringstream ostr; |
428 | StreamCopier::copyStream(rs, ostr); |
429 | assertTrue (ostr.str() == HTTPSTestServer::SMALL_BODY); |
430 | } |
431 | |
432 | |
433 | void HTTPSClientSessionTest::testServerAbort() |
434 | { |
435 | HTTPSTestServer srv; |
436 | HTTPSClientSession s("127.0.0.1" , srv.port()); |
437 | HTTPRequest request(HTTPRequest::HTTP_GET, "/nolength/connection/abort" ); |
438 | s.sendRequest(request); |
439 | HTTPResponse response; |
440 | std::istream& rs = s.receiveResponse(response); |
441 | assertTrue (response.getContentLength() == HTTPMessage::UNKNOWN_CONTENT_LENGTH); |
442 | assertTrue (response.getContentType() == "text/plain" ); |
443 | std::ostringstream ostr; |
444 | StreamCopier::copyStream(rs, ostr); |
445 | assertTrue (ostr.str() == HTTPSTestServer::SMALL_BODY); |
446 | assertTrue ( dynamic_cast<const Poco::Net::SSLConnectionUnexpectedlyClosedException*>( |
447 | s.networkException()) != NULL ); |
448 | } |
449 | |
450 | |
451 | void HTTPSClientSessionTest::setUp() |
452 | { |
453 | } |
454 | |
455 | |
456 | void HTTPSClientSessionTest::tearDown() |
457 | { |
458 | } |
459 | |
460 | |
461 | CppUnit::Test* HTTPSClientSessionTest::suite() |
462 | { |
463 | CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("HTTPSClientSessionTest" ); |
464 | |
465 | CppUnit_addTest(pSuite, HTTPSClientSessionTest, testGetSmall); |
466 | CppUnit_addTest(pSuite, HTTPSClientSessionTest, testGetLarge); |
467 | CppUnit_addTest(pSuite, HTTPSClientSessionTest, testHead); |
468 | CppUnit_addTest(pSuite, HTTPSClientSessionTest, testPostSmallIdentity); |
469 | CppUnit_addTest(pSuite, HTTPSClientSessionTest, testPostLargeIdentity); |
470 | CppUnit_addTest(pSuite, HTTPSClientSessionTest, testPostSmallChunked); |
471 | CppUnit_addTest(pSuite, HTTPSClientSessionTest, testPostLargeChunked); |
472 | CppUnit_addTest(pSuite, HTTPSClientSessionTest, testPostLargeChunkedKeepAlive); |
473 | CppUnit_addTest(pSuite, HTTPSClientSessionTest, testKeepAlive); |
474 | CppUnit_addTest(pSuite, HTTPSClientSessionTest, testInterop); |
475 | #ifdef FIXME |
476 | testProxy should use a public proxy server |
477 | http://www.publicproxyservers.com/proxy/list1.html |
478 | Really working public proxy servers - page 1 of 6. |
479 | #endif |
480 | CppUnit_addTest(pSuite, HTTPSClientSessionTest, testProxy); |
481 | CppUnit_addTest(pSuite, HTTPSClientSessionTest, testCachedSession); |
482 | CppUnit_addTest(pSuite, HTTPSClientSessionTest, testUnknownContentLength); |
483 | #if (POCO_OS != POCO_OS_CYGWIN) // FIXME temporary bypass |
484 | CppUnit_addTest(pSuite, HTTPSClientSessionTest, testServerAbort); |
485 | #endif |
486 | return pSuite; |
487 | } |
488 | |