1//
2// HTTPTestServer.cpp
3//
4// Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH.
5// and Contributors.
6//
7// SPDX-License-Identifier: BSL-1.0
8//
9
10
11#include "HTTPTestServer.h"
12#include "Poco/Net/StreamSocket.h"
13#include "Poco/Net/SocketAddress.h"
14#include "Poco/Timespan.h"
15#include "Poco/NumberFormatter.h"
16#include <iostream>
17
18
19using Poco::Net::Socket;
20using Poco::Net::StreamSocket;
21using Poco::Net::SocketAddress;
22using Poco::NumberFormatter;
23
24
25const std::string HTTPTestServer::SMALL_BODY("This is some random text data returned by the server");
26const std::string HTTPTestServer::LARGE_BODY(4000, 'x');
27
28
29HTTPTestServer::HTTPTestServer():
30 _socket(SocketAddress()),
31 _thread("HTTPTestServer"),
32 _stop(false)
33{
34 _thread.start(*this);
35 _ready.wait();
36 _lastRequest.reserve(4000);
37}
38
39
40HTTPTestServer::~HTTPTestServer()
41{
42 _stop = true;
43 _thread.join();
44}
45
46
47Poco::UInt16 HTTPTestServer::port() const
48{
49 return _socket.address().port();
50}
51
52
53const std::string& HTTPTestServer::lastRequest() const
54{
55 return _lastRequest;
56}
57
58
59void HTTPTestServer::run()
60{
61 _ready.set();
62 Poco::Timespan span(250000);
63 while (!_stop)
64 {
65 if (_socket.poll(span, Socket::SELECT_READ))
66 {
67 StreamSocket ss = _socket.acceptConnection();
68 try
69 {
70 _lastRequest.clear();
71 char buffer[256];
72 int n = ss.receiveBytes(buffer, sizeof(buffer));
73 while (n > 0 && !_stop)
74 {
75 _lastRequest.append(buffer, n);
76 if (!requestComplete())
77 n = ss.receiveBytes(buffer, sizeof(buffer));
78 else
79 n = 0;
80 }
81 std::string response = handleRequest();
82 ss.sendBytes(response.data(), (int) response.size());
83 Poco::Thread::sleep(1000);
84 try
85 {
86 ss.shutdown();
87 Poco::Thread::sleep(1000);
88 }
89 catch (Poco::Exception&)
90 {
91 }
92 }
93 catch (Poco::Exception& exc)
94 {
95 std::cerr << "HTTPTestServer: " << exc.displayText() << std::endl;
96 }
97 }
98 }
99}
100
101
102bool HTTPTestServer::requestComplete() const
103{
104 return ((_lastRequest.substr(0, 3) == "GET" || _lastRequest.substr(0, 4) == "HEAD") &&
105 (_lastRequest.find("\r\n\r\n") != std::string::npos)) ||
106 (_lastRequest.find("\r\n0\r\n") != std::string::npos);
107}
108
109
110std::string HTTPTestServer::handleRequest() const
111{
112 std::string response;
113 response.reserve(16000);
114 if (_lastRequest.substr(0, 10) == "GET /small" ||
115 _lastRequest.substr(0, 11) == "HEAD /small")
116 {
117 std::string body(SMALL_BODY);
118 response.append("HTTP/1.0 200 OK\r\n");
119 response.append("Content-Type: text/plain\r\n");
120 response.append("Content-Length: ");
121 response.append(NumberFormatter::format((int) body.size()));
122 response.append("\r\n");
123 response.append("Connection: Close\r\n");
124 response.append("\r\n");
125 if (_lastRequest.substr(0, 3) == "GET")
126 response.append(body);
127 }
128 else if (_lastRequest.substr(0, 10) == "GET /large" ||
129 _lastRequest.substr(0, 11) == "HEAD /large" ||
130 _lastRequest.substr(0, 36) == "GET http://www.somehost.com:80/large")
131 {
132 std::string body(LARGE_BODY);
133 response.append("HTTP/1.0 200 OK\r\n");
134 response.append("Content-Type: text/plain\r\n");
135 response.append("Content-Length: ");
136 response.append(NumberFormatter::format((int) body.size()));
137 response.append("\r\n");
138 response.append("Connection: Close\r\n");
139 response.append("\r\n");
140 if (_lastRequest.substr(0, 3) == "GET")
141 response.append(body);
142 }
143 else if (_lastRequest.substr(0, 12) == "POST /expect")
144 {
145 std::string::size_type pos = _lastRequest.find("\r\n\r\n");
146 pos += 4;
147 std::string body = _lastRequest.substr(pos);
148 response.append("HTTP/1.1 100 Continue\r\n\r\n");
149 response.append("HTTP/1.1 200 OK\r\n");
150 response.append("Content-Type: text/plain\r\n");
151 if (_lastRequest.find("Content-Length") != std::string::npos)
152 {
153 response.append("Content-Length: ");
154 response.append(NumberFormatter::format((int) body.size()));
155 response.append("\r\n");
156 }
157 else if (_lastRequest.find("chunked") != std::string::npos)
158 {
159 response.append("Transfer-Encoding: chunked\r\n");
160 }
161 response.append("Connection: Close\r\n");
162 response.append("\r\n");
163 response.append(body);
164 }
165 else if (_lastRequest.substr(0, 10) == "POST /fail")
166 {
167 std::string::size_type pos = _lastRequest.find("\r\n\r\n");
168 pos += 4;
169 std::string body = _lastRequest.substr(pos);
170 response.append("HTTP/1.1 400 Bad Request\r\n");
171 response.append("Connection: Close\r\n");
172 response.append("\r\n");
173 }
174 else if (_lastRequest.substr(0, 4) == "POST")
175 {
176 std::string::size_type pos = _lastRequest.find("\r\n\r\n");
177 pos += 4;
178 std::string body = _lastRequest.substr(pos);
179 response.append("HTTP/1.0 200 OK\r\n");
180 response.append("Content-Type: text/plain\r\n");
181 if (_lastRequest.find("Content-Length") != std::string::npos)
182 {
183 response.append("Content-Length: ");
184 response.append(NumberFormatter::format((int) body.size()));
185 response.append("\r\n");
186 }
187 else if (_lastRequest.find("chunked") != std::string::npos)
188 {
189 response.append("Transfer-Encoding: chunked\r\n");
190 }
191 response.append("Connection: Close\r\n");
192 response.append("\r\n");
193 response.append(body);
194 }
195 else if (_lastRequest.substr(0, 15) == "HEAD /keepAlive")
196 {
197 std::string body(SMALL_BODY);
198 response.append("HTTP/1.1 200 OK\r\n");
199 response.append("Connection: keep-alive\r\n");
200 response.append("Content-Type: text/plain\r\n");
201 response.append("Content-Length: ");
202 response.append(NumberFormatter::format((int) body.size()));
203 response.append("\r\n\r\n");
204 response.append("HTTP/1.1 200 OK\r\n");
205 response.append("Connection: Keep-Alive\r\n");
206 response.append("Content-Type: text/plain\r\n");
207 response.append("Content-Length: ");
208 response.append(NumberFormatter::format((int) body.size()));
209 response.append("\r\n\r\n");
210 response.append(body);
211 body = LARGE_BODY;
212 response.append("HTTP/1.1 200 OK\r\n");
213 response.append("Connection: keep-alive\r\n");
214 response.append("Content-Type: text/plain\r\n");
215 response.append("Transfer-Encoding: chunked\r\n\r\n");
216 response.append(NumberFormatter::formatHex((unsigned) body.length()));
217 response.append("\r\n");
218 response.append(body);
219 response.append("\r\n0\r\n\r\n");
220 response.append("HTTP/1.1 200 OK\r\n");
221 response.append("Connection: close\r\n");
222 response.append("Content-Type: text/plain\r\n");
223 response.append("Content-Length: ");
224 response.append(NumberFormatter::format((int) body.size()));
225 response.append("\r\n\r\n");
226 }
227 else if (_lastRequest.substr(0, 13) == "GET /redirect")
228 {
229 response.append("HTTP/1.0 302 Found\r\n");
230 response.append("Location: /large\r\n");
231 response.append("\r\n");
232 }
233 else if (_lastRequest.substr(0, 13) == "GET /notfound")
234 {
235 response.append("HTTP/1.0 404 Not Found\r\n");
236 response.append("\r\n");
237 }
238 else if (_lastRequest.substr(0, 5) == "GET /" ||
239 _lastRequest.substr(0, 6) == "HEAD /")
240 {
241 std::string body(SMALL_BODY);
242 response.append("HTTP/1.0 200 OK\r\n");
243 response.append("Content-Type: text/plain\r\n");
244 response.append("Content-Length: ");
245 response.append(NumberFormatter::format((int) body.size()));
246 response.append("\r\n");
247 response.append("Connection: Close\r\n");
248 response.append("\r\n");
249 if (_lastRequest.substr(0, 3) == "GET")
250 response.append(body);
251 }
252 return response;
253}
254