Atlas 0.7.0
Networking protocol for the Worldforge system.
Stream.cpp
1// This file may be redistributed and modified only under the terms of
2// the GNU Lesser General Public License (See COPYING for details).
3// Copyright (C) 2000 Michael Day, Dmitry Derevyanko
4
5// $Id$
6
7#include <Atlas/Net/Stream.h>
8
9#include <Atlas/Codecs/XML.h>
10#include <Atlas/Codecs/Packed.h>
11#include <Atlas/Codecs/Bach.h>
12
13#include <iostream>
14#include <memory>
15
16#define Debug(prg) { if (debug_flag) { prg } }
17
18static const bool debug_flag = false;
19
20static std::string get_line(std::string &s, char ch)
21{
22 std::string out;
23 int n = (int) s.find(ch);
24 if(n > 0)
25 {
26 out.assign(s, 0, (unsigned long) n);
27 s.erase(0, n+1UL);
28 }
29
30 return out;
31}
32
33static inline std::string get_line(std::string &s1, char ch, std::string &s2)
34{
35 s2 = get_line(s1, ch);
36
37 return s2;
38}
39
40namespace Atlas { namespace Net {
41
42NegotiateHelper::NegotiateHelper(std::list<std::string> & names) :
43 m_names(names)
44{
45}
46
47bool NegotiateHelper::get(std::string &buf, const std::string & header)
48{
49 std::string s, h;
50
51 while(!buf.empty())
52 {
53 // check for end condition
54 if(buf.find('\n') == 0)
55 {
56 buf.erase(0, 1);
57 return true;
58 }
59
60 if(get_line(buf, '\n', s).empty())
61 break;
62
63 if(get_line(s, ' ', h) == header)
64 {
65 m_names.push_back(s);
66
67 Debug( std::cout << " got: " << s << std::endl; )
68 }
69 else
70 Debug( std::cerr << "Unknown pattern " << h << std::endl; )
71 }
72 return false;
73}
74
75void NegotiateHelper::put(std::string &buf, const std::string & header)
76{
77 buf.erase();
78
79 buf += header;
80 buf += " Packed\n";
81
82 buf += header;
83 buf += " XML\n";
84
85 buf += header;
86 buf += " Bach\n";
87
88 buf += header;
89 buf += " Gzip\n";
90
91 buf += header;
92 buf += " Bzip2\n";
93
94 buf += "\n";
95}
96
97StreamConnect::StreamConnect(std::string name, std::istream& inStream, std::ostream& outStream) :
98 m_state(SERVER_GREETING), m_outName(std::move(name)), m_inStream(inStream), m_outStream(outStream),
99 m_codecHelper(m_inCodecs), m_filterHelper(m_inFilters),
100 m_canPacked(true), m_canXML(true), m_canBach(true),m_canGzip(true), m_canBzip2(true)
101{
102}
103
104void StreamConnect::poll()
105{
106 Debug( std::cout << "** Client(" << m_state << ") : " << m_inStream.rdbuf()->in_avail() << std::endl; )
107
108 std::string out;
109
110 std::streamsize count;
111 while ((count = m_inStream.rdbuf()->in_avail()) > 0) {
112 for (int i = 0 ; i < count; ++i) {
113 m_buf += (char) m_inStream.rdbuf()->sbumpc();
114 }
115 }
116
117 if(m_state == SERVER_GREETING)
118 {
119 // get server greeting
120
121 if (!m_buf.empty() && !get_line(m_buf, '\n', m_inName).empty())
122 {
123 Debug( std::cout << "server: " << m_inName << std::endl; )
124 m_state = CLIENT_GREETING;
125 }
126 }
127
128 if(m_state == CLIENT_GREETING)
129 {
130 // send client greeting
131
132 m_outStream << "ATLAS " << m_outName << std::endl;
133 m_state = CLIENT_CODECS;
134 }
135
136 if (m_state == CLIENT_CODECS)
137 {
138 //processClientCodecs();
139 m_codecHelper.put(out, "ICAN");
140 m_outStream << out << std::flush;
141 m_state = SERVER_CODECS;
142 }
143
144 if(m_state == SERVER_CODECS)
145 {
146 if (m_codecHelper.get(m_buf, "IWILL"))
147 {
148 processServerCodecs();
149 m_state = DONE;
150 }
151 }
152
153#if 0
154 if (m_state == CLIENT_FILTERS)
155 {
156 //processClientFilters();
157 m_filterHelper.put(out, "ICAN");
158 m_socket << out << std::flush;
159 m_state++;
160 }
161
162 if (m_state == SERVER_FILTERS)
163 {
164 if (m_filterHelper.get(m_buf, "IWILL"))
165 {
166 processServerFilters();
167 m_state++;
168 }
169 }
170#endif
171}
172
173Atlas::Negotiate::State StreamConnect::getState()
174{
175 if (m_state == DONE)
176 {
177 if (m_canPacked || m_canXML || m_canBach)
178 {
179 return SUCCEEDED;
180 }
181 }
182 else if (m_inStream || m_outStream)
183 {
184 return IN_PROGRESS;
185 }
186 return FAILED;
187}
188
190std::unique_ptr<Atlas::Codec> StreamConnect::getCodec(Atlas::Bridge & bridge)
191{
192 if (m_canPacked) { return std::make_unique<Atlas::Codecs::Packed>(m_inStream, m_outStream, bridge); }
193 if (m_canXML) { return std::make_unique<Atlas::Codecs::XML>(m_inStream, m_outStream, bridge); }
194 if (m_canBach) { return std::make_unique<Atlas::Codecs::Bach>(m_inStream, m_outStream, bridge); }
195 return {};
196}
197
198void StreamConnect::processServerCodecs()
199{
200 for (auto& codec : m_inCodecs)
201 {
202 if (codec == "XML") { m_canXML = true; }
203 if (codec == "Packed") { m_canPacked = true; }
204 if (codec == "Bach") { m_canBach = true; }
205 }
206}
207
208void StreamConnect::processServerFilters()
209{
210 for (auto& filter : m_inFilters)
211 {
212 if (filter == "Gzip") { m_canGzip = true; }
213 if (filter == "Bzip2") { m_canBzip2 = true; }
214 }
215}
216
217#if 0
218void StreamConnect::processClientCodecs()
219{
220 std::list<std::string>::const_iterator j;
221
222 for (j = m_inCodecs.begin(); j != m_inCodecs.end(); ++j)
223 {
224 // Do what?
225 }
226}
227
228void StreamConnect::processClientFilters()
229{
230 std::list<std::string>::const_iterator j;
231
232 for (j = m_inFilters.begin(); j != m_inFilters.end(); ++j)
233 {
234 // Do what?
235 }
236}
237#endif
238
239
240StreamAccept::StreamAccept(std::string name, std::istream& inStream, std::ostream& outStream) :
241 m_state(SERVER_GREETING),
242 m_outName(std::move(name)),
243 m_inStream(inStream),
244 m_outStream(outStream),
245 m_codecHelper(m_inCodecs),
246 m_filterHelper(m_inFilters),
247 m_canPacked(false),
248 m_canXML(false),
249 m_canBach(false),
250 m_canGzip(false),
251 m_canBzip2(false)
252{
253}
254
255void StreamAccept::poll()
256{
257 Debug( std::cout << "** Server(" << m_state << ") : " << std::endl; )
258
259 if (m_state == SERVER_GREETING)
260 {
261 // send server greeting
262
263 m_outStream << "ATLAS " << m_outName << std::endl;
264 m_state = CLIENT_GREETING;
265 Debug( std::cout << "server now in state " << m_state << std::endl; )
266 }
267
268 std::streamsize count;
269 while ((count = m_inStream.rdbuf()->in_avail()) > 0) {
270 for (int i = 0 ; i < count; ++i) {
271 m_buf += (char) m_inStream.rdbuf()->sbumpc();
272 }
273 }
274
275 if (m_state == CLIENT_GREETING)
276 {
277 // get client greeting
278 if (!m_buf.empty() && !get_line(m_buf, '\n', m_inName).empty())
279 {
280 Debug(std::cout << "client: " << m_inName << std::endl; )
281 m_state = CLIENT_CODECS;
282 }
283 }
284
285 if (m_state == CLIENT_CODECS)
286 {
287 if (m_codecHelper.get(m_buf, "ICAN"))
288 {
289 m_state = SERVER_CODECS;
290 Debug(std::cout << "server now in state " << m_state << std::endl;)
291 }
292 processClientCodecs();
293 }
294
295 if (m_state == SERVER_CODECS)
296 {
297 if (m_canPacked) { m_outStream << "IWILL Packed\n"; }
298 else if (m_canXML) { m_outStream << "IWILL XML\n"; }
299 else if (m_canBach) { m_outStream << "IWILL Bach\n"; }
300 m_outStream << std::endl;
301
302 m_state = DONE;
303 }
304
305#if 0
306 if(m_state == CLIENT_FILTERS)
307 {
308 if (m_filterHelper.get(m_buf, "ICAN"))
309 {
310 m_state++;
311 }
312 processClientFilters();
313 }
314
315 if (m_state == SERVER_FILTERS)
316 {
317 //No Filters until they actually work.
318 //if (m_canGzip) { m_socket << "IWILL Gzip\n"; }
319 //else if (m_canBzip2) { m_socket << "IWILL Bzip2\n"; }
320 m_outStream << std::endl;
321 m_state++;
322 }
323#endif
324}
325
326Atlas::Negotiate::State StreamAccept::getState()
327{
328 if (m_state == DONE)
329 {
330 if (m_canPacked || m_canXML || m_canBach)
331 {
332 return SUCCEEDED;
333 }
334
335 std::cout << "done, but no codec" << std::endl;
336 }
337 else if (m_inStream || m_outStream)
338 {
339 return IN_PROGRESS;
340 }
341 return FAILED;
342}
343
345std::unique_ptr<Atlas::Codec> StreamAccept::getCodec(Atlas::Bridge & bridge)
346{
347 // XXX XXX XXX XXX
348 // should pass an appropriate filterbuf here instead of socket,
349 // if we found a filter of course.
350 // this poses the problem of the filter being passed by
351 // reference, so we'd have to allocate on the heap, but then who
352 // would deallocate? erk. -- sdt 2001-01-05
353 //return (*outCodecs.begin())->
354 //New(Codec<std::iostream>::Parameters(m_socket,bridge));
355 if (m_canPacked) { return std::make_unique<Atlas::Codecs::Packed>(m_inStream, m_outStream, bridge); }
356 if (m_canXML) { return std::make_unique<Atlas::Codecs::XML>(m_inStream, m_outStream, bridge); }
357 if (m_canBach) { return std::make_unique<Atlas::Codecs::Bach>(m_inStream, m_outStream, bridge); }
358 return nullptr;
359}
360
361#if 0
362void StreamAccept::processServerCodecs()
363{
364 FactoryCodecs::iterator i;
365 list<std::string>::iterator j;
366
367 FactoryCodecs *myCodecs = Factory<Codec<std::iostream> >::factories();
368
369 for (i = myCodecs->begin(); i != myCodecs->end(); ++i)
370 {
371 for (j = m_inCodecs.begin(); j != m_inCodecs.end(); ++j)
372 {
373 if ((*i)->getName() == *j)
374 {
375 outCodecs.push_back(*i);
376 return;
377 }
378 }
379 }
380}
381
382void StreamAccept::processServerFilters()
383{
384 FactoryFilters::iterator i;
385 list<std::string>::iterator j;
386
387 FactoryFilters *myFilters = Factory<Filter>::factories();
388
389 for (i = myFilters->begin(); i != myFilters->end(); ++i)
390 {
391 for (j = m_inFilters.begin(); j != m_inFilters.end(); ++j)
392 {
393 if ((*i)->getName() == *j)
394 {
395 outFilters.push_back(*i);
396 }
397 }
398 }
399}
400#endif
401
402void StreamAccept::processClientCodecs()
403{
404 for (auto& codec : m_inCodecs)
405 {
406 if (codec == "XML") { m_canXML = true; }
407 if (codec == "Packed") { m_canPacked = true; }
408 if (codec == "Bach") { m_canBach = true; }
409 }
410}
411
412void StreamAccept::processClientFilters()
413{
414 for (auto& filter : m_inFilters)
415 {
416 if (filter == "Gzip") { m_canGzip = true; }
417 if (filter == "Bzip2") { m_canBzip2 = true; }
418 }
419}
420
421} } // namespace Atlas Net
std::unique_ptr< Atlas::Codec > getCodec(Atlas::Bridge &) override
FIXME We should pass in the Bridge here, not at construction time.
Definition: Stream.cpp:345
Definition: Bridge.h:20