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 | |
19 | using Poco::Net::Socket; |
20 | using Poco::Net::StreamSocket; |
21 | using Poco::Net::SocketAddress; |
22 | using Poco::NumberFormatter; |
23 | |
24 | |
25 | const std::string HTTPTestServer::SMALL_BODY("This is some random text data returned by the server" ); |
26 | const std::string HTTPTestServer::LARGE_BODY(4000, 'x'); |
27 | |
28 | |
29 | HTTPTestServer::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 | |
40 | HTTPTestServer::~HTTPTestServer() |
41 | { |
42 | _stop = true; |
43 | _thread.join(); |
44 | } |
45 | |
46 | |
47 | Poco::UInt16 HTTPTestServer::port() const |
48 | { |
49 | return _socket.address().port(); |
50 | } |
51 | |
52 | |
53 | const std::string& HTTPTestServer::lastRequest() const |
54 | { |
55 | return _lastRequest; |
56 | } |
57 | |
58 | |
59 | void 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 | |
102 | bool 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 | |
110 | std::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 | |