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