]> git.mxchange.org Git - simgear.git/blob - simgear/io/test_HTTP.hxx
Improving channel lifetime in HTTP-based tests.
[simgear.git] / simgear / io / test_HTTP.hxx
1 #ifndef SIMGEAR_IO_TEST_HTTP_HXX
2 #define SIMGEAR_IO_TEST_HTTP_HXX
3
4 #include <sstream>
5 #include <vector>
6
7 #include <simgear/io/sg_netChat.hxx>
8 #include <simgear/misc/strutils.hxx>
9
10 namespace simgear
11 {
12
13 class TestServerChannel : public NetChat
14 {
15 public:
16     enum State
17     {
18         STATE_IDLE = 0,
19         STATE_HEADERS,
20         STATE_CLOSING,
21         STATE_REQUEST_BODY
22     };
23
24     TestServerChannel()
25     {
26         state = STATE_IDLE;
27         setTerminator("\r\n");
28
29     }
30
31     virtual ~TestServerChannel()
32     {
33         std::cerr << "dtor test server channel" << std::endl;
34     }
35
36     virtual void collectIncomingData(const char* s, int n)
37     {
38         buffer += std::string(s, n);
39     }
40
41     virtual void foundTerminator(void)
42     {
43         if (state == STATE_IDLE) {
44             state = STATE_HEADERS;
45             string_list line = strutils::split(buffer, NULL, 3);
46             if (line.size() < 3) {
47                 std::cerr << "malformed request:" << buffer << std::endl;
48                 exit(-1);
49             }
50
51             method = line[0];
52             path = line[1];
53
54             std::string::size_type queryPos = path.find('?');
55             if (queryPos != std::string::npos) {
56                 parseArgs(path.substr(queryPos + 1));
57                 path = path.substr(0, queryPos);
58             }
59
60             httpVersion = line[2];
61             requestHeaders.clear();
62             buffer.clear();
63         } else if (state == STATE_HEADERS) {
64             std::string s = strutils::simplify(buffer);
65             if (s.empty()) {
66                 buffer.clear();
67                 receivedRequestHeaders();
68                 return;
69             }
70
71             std::string::size_type colonPos = buffer.find(':');
72             if (colonPos == std::string::npos) {
73                 std::cerr << "test malformed HTTP response header:" << buffer << std::endl;
74                 buffer.clear();
75                 return;
76             }
77
78             std::string key = strutils::simplify(buffer.substr(0, colonPos));
79             std::string value = strutils::strip(buffer.substr(colonPos + 1));
80             requestHeaders[key] = value;
81             buffer.clear();
82         } else if (state == STATE_REQUEST_BODY) {
83             receivedBody();
84             setTerminator("\r\n");
85         } else if (state == STATE_CLOSING) {
86           // ignore!
87         }
88     }
89
90     void parseArgs(const std::string& argData)
91     {
92         string_list argv = strutils::split(argData, "&");
93         for (unsigned int a=0; a<argv.size(); ++a) {
94             std::string::size_type eqPos = argv[a].find('=');
95             if (eqPos == std::string::npos) {
96                 std::cerr << "malformed HTTP argument:" << argv[a] << std::endl;
97                 continue;
98             }
99
100             std::string key = argv[a].substr(0, eqPos);
101             std::string value = argv[a].substr(eqPos + 1);
102             args[key] = value;
103         }
104     }
105
106     void receivedRequestHeaders()
107     {
108         state = STATE_IDLE;
109         processRequestHeaders();
110     }
111
112     virtual void processRequestHeaders()
113     {
114       sendErrorResponse(404, false, "");
115     }
116
117     void closeAfterSending()
118     {
119       state = STATE_CLOSING;
120       closeWhenDone();
121     }
122
123     void receivedBody()
124     {
125         state = STATE_IDLE;
126         if (method == "POST") {
127             parseArgs(buffer);
128         }
129
130         processRequestBody();
131
132         buffer.clear();
133     }
134
135     virtual void processRequestBody()
136     {
137       sendErrorResponse(404, false, "");
138     }
139
140     void sendErrorResponse(int code, bool close, std::string content)
141     {
142         std::cerr << "sending error " << code << " for " << path << std::endl;
143         std::cerr << "\tcontent:" << content << std::endl;
144
145         std::stringstream headerData;
146         headerData << "HTTP/1.1 " << code << " " << reasonForCode(code) << "\r\n";
147         headerData << "Content-Length:" << content.size() << "\r\n";
148         headerData << "\r\n"; // final CRLF to terminate the headers
149         push(headerData.str().c_str());
150         push(content.c_str());
151
152         if (close) {
153             closeWhenDone();
154         }
155     }
156
157     std::string reasonForCode(int code)
158     {
159         switch (code) {
160             case 200: return "OK";
161             case 201: return "Created";
162             case 204: return "no content";
163             case 404: return "not found";
164             case 407: return "proxy authentication required";
165             default: return "unknown code";
166         }
167     }
168
169     virtual void handleClose (void)
170     {
171         std::cerr << "channel close" << std::endl;
172         NetBufferChannel::handleClose();
173     }
174
175
176     State state;
177     std::string buffer;
178     std::string method;
179     std::string path;
180     std::string httpVersion;
181     std::map<std::string, std::string> requestHeaders;
182     std::map<std::string, std::string> args;
183     int requestContentLength;
184 };
185
186 class EraseIfClosed
187 {
188 public:
189     bool operator()(simgear::NetChannel* chan) const
190     {
191         return chan->isClosed();
192     }
193 };
194
195 template <class T>
196 class TestServer : public NetChannel
197 {
198     simgear::NetChannelPoller _poller;
199     std::vector<T*> _channels;
200 public:
201     TestServer()
202     {
203         Socket::initSockets();
204
205
206         open();
207         bind(NULL, 2000); // localhost, any port
208         listen(16);
209
210         _poller.addChannel(this);
211     }
212
213     virtual ~TestServer()
214     {
215     }
216
217     virtual bool writable (void) { return false ; }
218
219     virtual void handleAccept (void)
220     {
221         simgear::IPAddress addr ;
222         int handle = accept ( &addr ) ;
223         T* chan = new T();
224         chan->setHandle(handle);
225
226         _channels.push_back(chan);
227         _poller.addChannel(chan);
228     }
229
230     void poll()
231     {
232         _poller.poll();
233
234         typename std::vector<T*>::iterator it;
235         it = std::remove_if(_channels.begin(), _channels.end(), EraseIfClosed());
236
237         for (typename std::vector<T*>::iterator it2 = it; it2 != _channels.end(); ++it2) {
238             _poller.removeChannel(*it2);
239             delete *it2;
240         }
241
242         _channels.erase(it, _channels.end());
243
244     }
245
246     int connectCount()
247     {
248         return _channels.size();
249     }
250
251     void disconnectAll()
252     {
253         typename std::vector<T*>::iterator it;
254         for (it = _channels.begin(); it != _channels.end(); ++it) {
255             _poller.removeChannel(*it);
256             delete *it;
257         }
258
259         _channels.clear();
260     }
261 };
262
263 } // of namespace simgear
264
265 #endif // of SIMGEAR_IO_TEST_HTTP_HXX