]> git.mxchange.org Git - simgear.git/blob - simgear/io/test_HTTP.hxx
HTTP repository implementation
[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 public:
178     TestServer()
179     {
180         Socket::initSockets();
181
182         open();
183         bind(NULL, 2000); // localhost, any port
184         listen(5);
185
186         _poller.addChannel(this);
187     }
188
189     virtual ~TestServer()
190     {
191     }
192
193     virtual bool writable (void) { return false ; }
194
195     virtual void handleAccept (void)
196     {
197         simgear::IPAddress addr ;
198         int handle = accept ( &addr ) ;
199         TestServerChannel* chan = new T();
200         chan->setHandle(handle);
201
202         _poller.addChannel(chan);
203     }
204
205     void poll()
206     {
207         _poller.poll();
208     }
209 };
210
211 } // of namespace simgear
212
213 #endif // of SIMGEAR_IO_TEST_HTTP_HXX