EvEmu  0.8.4
11 September 2021
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
APIServerConnection Class Reference

Handles a client connection to the API server. More...

#include "APIServerConnection.h"

Inheritance diagram for APIServerConnection:
Collaboration diagram for APIServerConnection:

Public Member Functions

void Process ()
 
boost::asio::ip::tcp::socket & socket ()
 

Static Public Member Functions

static std::tr1::shared_ptr
< APIServerConnection
create (boost::asio::io_context &io)
 

Private Member Functions

 APIServerConnection (boost::asio::io_context &io)
 
void ProcessHeaders ()
 
void ProcessPostData ()
 
void SendXML ()
 
void NotFound ()
 
void Close ()
 
void Redirect ()
 
void RedirectLocation ()
 
void RedirectFinalize ()
 

Static Private Member Functions

static bool starts_with (std::string &haystack, const char *const needle)
 

Private Attributes

std::string _service
 
std::string _service_handler
 
std::string _redirectUrl
 
std::string _http_cmd_str
 
APICommandCall m_apiCommandCall
 
boost::asio::streambuf _buffer
 
boost::asio::streambuf _postBuffer
 
boost::asio::ip::tcp::socket _socket
 
std::tr1::shared_ptr
< std::vector< char > > 
_xmlData
 

Static Private Attributes

static boost::asio::const_buffers_1 _responseOK = boost::asio::buffer("HTTP/1.0 200 OK\r\nContent-Type: text/xml\r\n\r\n", 43)
 
static boost::asio::const_buffers_1 _responseNotFound
 
static boost::asio::const_buffers_1 _responseNoContent = boost::asio::buffer("HTTP/1.0 204 No Content", 23)
 
static boost::asio::const_buffers_1 _responseRedirectBegin = boost::asio::buffer("HTTP/1.0 301 Moved Permanently\r\nLocation: ", 42)
 
static boost::asio::const_buffers_1 _responseRedirectEnd = boost::asio::buffer("\r\n\r\n", 4)
 

Detailed Description

Handles a client connection to the API server.

Handles exactly one client; does all the protocol related stuff. Very limited HTTP handling.

Author
Aknor Jaden
Date
July 2011

Definition at line 41 of file APIServerConnection.h.

Constructor & Destructor Documentation

APIServerConnection::APIServerConnection ( boost::asio::io_context &  io)
private

Definition at line 71 of file APIServerConnection.cpp.

Referenced by create().

72  : _socket(io)
73 {
74 }
boost::asio::ip::tcp::socket _socket

Here is the caller graph for this function:

Member Function Documentation

void APIServerConnection::Close ( )
private

Definition at line 418 of file APIServerConnection.cpp.

References _socket.

Referenced by NotFound(), ProcessPostData(), RedirectFinalize(), and SendXML().

419 {
420  _socket.close();
421 }
boost::asio::ip::tcp::socket _socket

Here is the caller graph for this function:

std::tr1::shared_ptr< APIServerConnection > APIServerConnection::create ( boost::asio::io_context &  io)
static

Definition at line 428 of file APIServerConnection.cpp.

References APIServerConnection().

Referenced by APIServerListener::StartAccept().

429 {
430  return std::tr1::shared_ptr<APIServerConnection>(new APIServerConnection(io));
431 }
APIServerConnection(boost::asio::io_context &io)

Here is the call graph for this function:

Here is the caller graph for this function:

void APIServerConnection::NotFound ( )
private

Definition at line 394 of file APIServerConnection.cpp.

References _responseNotFound, _socket, and Close().

Referenced by ProcessHeaders(), and ProcessPostData().

395 {
396  boost::asio::async_write(_socket, _responseNotFound, boost::asio::transfer_all(), std::bind(&APIServerConnection::Close, shared_from_this()));
397 }
static boost::asio::const_buffers_1 _responseNotFound
boost::asio::ip::tcp::socket _socket

Here is the call graph for this function:

Here is the caller graph for this function:

void APIServerConnection::Process ( )

Definition at line 81 of file APIServerConnection.cpp.

References _buffer, _socket, and ProcessHeaders().

82 {
83  // receive all HTTP headers from the client
84  boost::asio::async_read_until(_socket, _buffer, "\r\n\r\n", std::bind(&APIServerConnection::ProcessHeaders, shared_from_this()));
85 }
boost::asio::streambuf _buffer
boost::asio::ip::tcp::socket _socket

Here is the call graph for this function:

void APIServerConnection::ProcessHeaders ( )
private

Definition at line 87 of file APIServerConnection.cpp.

References _buffer, _http_cmd_str, _postBuffer, _responseOK, _service, _service_handler, _socket, _xmlData, m_apiCommandCall, NotFound(), ProcessPostData(), sAPIServer, SendXML(), sLog, and starts_with().

Referenced by Process().

88 {
89  std::istream stream(&_buffer);
90  std::string query;
91  std::string request;
92  std::string get_chk_str;
93  std::string post_chk_str;
94  int pos;
95  int parameterCount;
96  std::string param;
97  std::string value;
98 
99  // Clear API Command Call container:
100  m_apiCommandCall.clear();
101 
102  // Get the first header line, every request line ends with "\r\n"
103  // GET /service/ServiceHandler.xml.aspx?param1=value&param2=value&param3=value HTTP/1.0\r\n
104  // POST /service/ServiceHandler.xml.aspx HTTP/1.0\r\n
105  std::getline(stream, request, '\r');
106  query = request;
107 
108  get_chk_str = request.substr(0,3);
109  post_chk_str = request.substr(0,4);
110 
111  if (get_chk_str.compare("GET") == 0)
112  {
113  sLog.Debug( "APIServerConnection::ProcessHeaders()", "RECEIVED new HTTP GET request..." );
114 
115  // Format of an HTTP GET query:
116  // 0 5 10 20
117  // GET /service/ServiceHandler.xml.aspx?param1=value&param2=value&param3=value HTTP/1.0\r\n
118  _http_cmd_str = get_chk_str;
119 
120  request = request.substr(4); // Strip off the "GET " prefix
121 
122  // Find first space at end of header, if there is one, and strip off the rest of the line, ie the " HTTP/1.0\r\n" string
123  int del = request.find_first_of(' ');
124  if (del == std::string::npos)
125  {
126  NotFound();
127  return;
128  }
129  request = request.substr(0,del);
130 
131  if (!starts_with(request, "/"))
132  {
133  NotFound();
134  return;
135  }
136  request = request.substr(1);
137 
138  pos = request.find_first_of('/');
139  _service = request.substr(0,pos);
140  m_apiCommandCall.insert( std::pair<std::string, std::string>( "service", _service ) );
141  request = request.substr(pos+1);
142  pos = request.find_first_of('?');
143  _service_handler = request.substr(0,pos);
144  m_apiCommandCall.insert( std::pair<std::string, std::string>( "servicehandler", _service_handler ) );
145  request = request.substr(pos+1);
146 
148  // Parse the query portion of the GET to a series of string pairs ("param", "value") from the URI
149  while( (pos = request.find_first_of('=')) >= 0 )
150  {
151  param = request.substr(0,pos);
152  std::transform(param.begin(), param.end(), param.begin(), tolower);
153  request = request.substr(pos+1);
154  pos = request.find_first_of('&');
155  if( pos < 0 )
156  value = request;
157  else
158  {
159  value = request.substr(0,pos);
160  request = request.substr(pos);
161  if( request.substr(0,5) == "&amp;" ) // Strip out "&amp;" encoded for "&"
162  request = request.substr(5);
163  else
164  request = request.substr(1);
165  }
166  m_apiCommandCall.insert( std::pair<std::string, std::string>( param, value ) );
167  }
168 
170  if (!_xmlData)
171  {
172  sLog.Error("APIServerConnection::ProcessHeaders()", "Unknown or malformed EVEmu API HTTP CMD Received:\r\n%s\r\n", query.c_str());
173  NotFound();
174  return;
175  }
176 
177  // Print out to the Log with basic info on the API call and all parameters and their values parsed out
178  sLog.Debug("APIServerConnection::ProcessHeaders()", "HTTP %s CMD Received: Service: %s, Handler: %s", _http_cmd_str.c_str(), _service.c_str(), _service_handler.c_str());
179  APICommandCall::const_iterator cur, end;
180  cur = m_apiCommandCall.begin();
181  end = m_apiCommandCall.end();
182  for (int i=1; cur != end; cur++, i++)
183  sLog.Debug(" ", "%d: param = %s, value = %s", i, cur->first.c_str(), cur->second.c_str() );
184 
185  // first we have to send the responseOK, then our actual result
186  boost::asio::async_write(_socket, _responseOK, boost::asio::transfer_all(), std::bind(&APIServerConnection::SendXML, shared_from_this()));
188  }
189  else if (post_chk_str.compare("POST") == 0)
190  {
191  sLog.Debug( "APIServerConnection::ProcessHeaders()", "RECEIVED new HTTP POST request..." );
192 
193  // Format of an HTTP GET query:
194  //
195  // POST /service/ServiceHandler.xml.aspx HTTP/1.0\r\n
196  // Content-Type: application/x-www-form-urlencoded\r\n
197  // Host: api.eve-online.com\r\n
198  // Content-Length: 86\r\n
199  // \r\n
200  // param1=value&param2=value&param3=value\r\n
201  // \r\n
202 
203  _http_cmd_str = post_chk_str;
204 
205  request = request.substr(5); // Strip off the "POST " prefix
206 
207  // Find first space at end of header, if there is one, and strip off the rest of the line, ie the " HTTP/1.0\r\n" string
208  int del = request.find_first_of(' ');
209  if (del == std::string::npos)
210  {
211  NotFound();
212  return;
213  }
214  request = request.substr(0,del);
215 
216  if (!starts_with(request, "/"))
217  {
218  NotFound();
219  return;
220  }
221  request = request.substr(1);
222 
223  pos = request.find_first_of('/');
224  _service = request.substr(0,pos);
225  m_apiCommandCall.insert( std::pair<std::string, std::string>( "service", _service ) );
226  request = request.substr(pos+1);
227  _service_handler = request;
228  m_apiCommandCall.insert( std::pair<std::string, std::string>( "servicehandler", _service_handler ) );
229 
230  // Read the integer after 'Content-Length:' and use that as the # of bytes in the next step
231  while( (request.substr( 0,15 ).compare( "Content-Length:" )) != 0 )
232  {
233  std::getline(stream, request, '\r');
234  request = request.substr(1);
235  query += request;
236  }
237  pos = request.find_first_of(' ');
238  request = request.substr( pos+1 );
239  uint32 postDataBytes = atoi( request.c_str() );
240  std::getline(stream, request, '\n');
241 
242  sLog.Debug( "APIServerConnection::ProcessHeaders()", " POST Content-Length = %u bytes", postDataBytes );
243 
244  // Keep reading lines until we get past the next "\r\n" line (blank line):
245  while( request.compare( "\r" ) != 0 )
246  {
247  std::getline(stream, request, '\n');
248  }
249 
250  std::getline(stream, request, '\n');
251  pos = request.find_first_of('\r');
252  request = request.substr( 0,pos );
253 
254  if( request.compare( "" ) != 0 )
255  {
256  // Decode the arguments of the POST data block here since asio did NOT stop reading past the first "\r\n\r\n"
258  // Parse the query portion of the GET to a series of string pairs ("param", "value") from the URI
259  sLog.Debug( "APIServerConnection::ProcessHeaders()", "POST data found in ProcessHeaders() ! Parsing..." );
260  parameterCount = 0;
261  while( (pos = request.find_first_of('=')) >= 0 )
262  {
263  parameterCount++;
264  param = request.substr(0,pos);
265  std::transform(param.begin(), param.end(), param.begin(), tolower);
266  request = request.substr(pos+1);
267  pos = request.find_first_of('&');
268  if( pos < 0 )
269  value = request;
270  else
271  {
272  value = request.substr(0,pos);
273  request = request.substr(pos);
274  if( request.substr(0,5) == "&amp;" ) // Strip out "&amp;" encoded for "&"
275  request = request.substr(5);
276  else
277  request = request.substr(1);
278  }
279  m_apiCommandCall.insert( std::pair<std::string, std::string>( param, value ) );
280  }
281 
282  // Did we somehow not detect a lack of POST data? If so, and NO parameters were recovered, queue up the trigger for PostProcessHeaders():
283  if( parameterCount == 0 )
284  {
285  // Call boost::asio::async_read() and feed it the # of bytes from step 1) to get the POST data
286  // The 'CompleteCondition' for THIS boost::asio::async_read, a parameter that specifies when to stop reading,
287  // is transfer_exactly(contentLength), where contentLength is the # of bytes we just recovered from the "Content-Length" header
288  boost::asio::async_read(_socket, _postBuffer, boost::asio::transfer_exactly(postDataBytes), std::bind(&APIServerConnection::ProcessPostData, shared_from_this()));
289  }
290 
292  if (!_xmlData)
293  {
294  sLog.Error("APIServerConnection::ProcessHeaders()", "Unknown or malformed EVEmu API HTTP CMD Received:\r\n%s\r\n", query.c_str());
295  NotFound();
296  return;
297  }
298 
299  // Print out to the Log with basic info on the API call and all parameters and their values parsed out
300  sLog.Debug("APIServerConnection::ProcessHeaders()", "HTTP %s CMD Received: Service: %s, Handler: %s", _http_cmd_str.c_str(), _service.c_str(), _service_handler.c_str());
301  APICommandCall::const_iterator cur, end;
302  cur = m_apiCommandCall.begin();
303  end = m_apiCommandCall.end();
304  for (int i=1; cur != end; cur++, i++)
305  sLog.Debug(" ", "%d: param = %s, value = %s", i, cur->first.c_str(), cur->second.c_str() );
306 
307  // first we have to send the responseOK, then our actual result
308  boost::asio::async_write(_socket, _responseOK, boost::asio::transfer_all(), std::bind(&APIServerConnection::SendXML, shared_from_this()));
310  }
311  else
312  {
313  // Call boost::asio::async_read() and feed it the # of bytes from step 1) to get the POST data
314  // The 'CompleteCondition' for THIS boost::asio::async_read, a parameter that specifies when to stop reading,
315  // is transfer_exactly(contentLength), where contentLength is the # of bytes we just recovered from the "Content-Length" header
316  boost::asio::async_read(_socket, _postBuffer, boost::asio::transfer_exactly(postDataBytes), std::bind(&APIServerConnection::ProcessPostData, shared_from_this()));
317  }
318  }
319  else
320  {
321  NotFound();
322  return;
323  }
324 }
boost::asio::streambuf _postBuffer
#define sAPIServer
Definition: APIServer.h:85
static boost::asio::const_buffers_1 _responseOK
APICommandCall m_apiCommandCall
std::tr1::shared_ptr< std::vector< char > > _xmlData
#define sLog
Evaluates to a NewLog instance.
Definition: LogNew.h:250
unsigned __int32 uint32
Definition: eve-compat.h:50
boost::asio::streambuf _buffer
static bool starts_with(std::string &haystack, const char *const needle)
boost::asio::ip::tcp::socket _socket

Here is the call graph for this function:

Here is the caller graph for this function:

void APIServerConnection::ProcessPostData ( )
private

Definition at line 326 of file APIServerConnection.cpp.

References _http_cmd_str, _postBuffer, _responseNoContent, _responseOK, _service, _service_handler, _socket, _xmlData, Close(), m_apiCommandCall, NotFound(), sAPIServer, SendXML(), and sLog.

Referenced by ProcessHeaders().

327 {
328  std::istream stream(&_postBuffer);
329  std::string query;
330  std::string request;
331  int pos;
332  std::string param;
333  std::string value;
334 
335  std::getline(stream, request, '\r');
336 
337  // Check for empty POST data block, and if empty, return without sending anything back:
338  if( request.compare( "" ) == 0 )
339  {
340  sLog.Error("APIServerConnection::ProcessPostData()", "POST data block is COMPLETELY EMPTY!!" );
341  boost::asio::async_write(_socket, _responseNoContent, boost::asio::transfer_all(), std::bind(&APIServerConnection::Close, shared_from_this()));
342  //NotFound();
343  return;
344  }
345 
346  _http_cmd_str += request;
347 
348  // Parse the data portion of the POST to a series of string pairs ("param", "value") from the URI
349  while( (pos = request.find_first_of('=')) >= 0 )
350  {
351  param = request.substr(0,pos);
352  std::transform(param.begin(), param.end(), param.begin(), tolower);
353  request = request.substr(pos+1);
354  pos = request.find_first_of('&');
355  if( pos < 0 )
356  value = request;
357  else
358  {
359  value = request.substr(0,pos);
360  request = request.substr(pos);
361  if( request.substr(0,5) == "&amp;" ) // Strip out "&amp;" encoded for "&"
362  request = request.substr(5);
363  else
364  request = request.substr(1);
365  }
366  m_apiCommandCall.insert( std::pair<std::string, std::string>( param, value ) );
367  }
368 
370  if (!_xmlData)
371  {
372  sLog.Error("APIServerConnection::ProcessPostData()", "Unknown or malformed EVEmu API HTTP CMD Received:\r\n%s\r\n", query.c_str());
373  NotFound();
374  return;
375  }
376 
377  // Print out to the Log with basic info on the API call and all parameters and their values parsed out
378  sLog.Debug("APIServerConnection::ProcessPostData()", "HTTP %s CMD Received: Service: %s, Handler: %s", _http_cmd_str.c_str(), _service.c_str(), _service_handler.c_str());
379  APICommandCall::const_iterator cur, end;
380  cur = m_apiCommandCall.begin();
381  end = m_apiCommandCall.end();
382  for (int i=1; cur != end; cur++, i++)
383  sLog.Debug(" ", "%d: param = %s, value = %s", i, cur->first.c_str(), cur->second.c_str() );
384 
385  // first we have to send the responseOK, then our actual result
386  boost::asio::async_write(_socket, _responseOK, boost::asio::transfer_all(), std::bind(&APIServerConnection::SendXML, shared_from_this()));
387 }
boost::asio::streambuf _postBuffer
#define sAPIServer
Definition: APIServer.h:85
static boost::asio::const_buffers_1 _responseOK
APICommandCall m_apiCommandCall
std::tr1::shared_ptr< std::vector< char > > _xmlData
#define sLog
Evaluates to a NewLog instance.
Definition: LogNew.h:250
static boost::asio::const_buffers_1 _responseNoContent
boost::asio::ip::tcp::socket _socket

Here is the call graph for this function:

Here is the caller graph for this function:

void APIServerConnection::Redirect ( )
private

Definition at line 399 of file APIServerConnection.cpp.

References _responseRedirectBegin, _socket, and RedirectLocation().

400 {
401  boost::asio::async_write(_socket, _responseRedirectBegin, boost::asio::transfer_all(), std::bind(&APIServerConnection::RedirectLocation, shared_from_this()));
402 }
static boost::asio::const_buffers_1 _responseRedirectBegin
boost::asio::ip::tcp::socket _socket

Here is the call graph for this function:

void APIServerConnection::RedirectFinalize ( )
private

Definition at line 413 of file APIServerConnection.cpp.

References _responseRedirectEnd, _socket, and Close().

Referenced by RedirectLocation().

414 {
415  boost::asio::async_write(_socket, _responseRedirectEnd, boost::asio::transfer_all(), std::bind(&APIServerConnection::Close, shared_from_this()));
416 }
static boost::asio::const_buffers_1 _responseRedirectEnd
boost::asio::ip::tcp::socket _socket

Here is the call graph for this function:

Here is the caller graph for this function:

void APIServerConnection::RedirectLocation ( )
private

Definition at line 404 of file APIServerConnection.cpp.

References _redirectUrl, _service, _socket, APIServer::FallbackURL, and RedirectFinalize().

Referenced by Redirect().

405 {
406  //std::string extension = _service == "Character" ? "jpg" : "png";
407  std::stringstream url;
408  url << APIServer::FallbackURL << _service;// << "/" << _id;
409  _redirectUrl = url.str();
410  boost::asio::async_write(_socket, boost::asio::buffer(_redirectUrl), boost::asio::transfer_all(), std::bind(&APIServerConnection::RedirectFinalize, shared_from_this()));
411 }
static const char *const FallbackURL
Definition: APIServer.h:58
boost::asio::ip::tcp::socket _socket

Here is the call graph for this function:

Here is the caller graph for this function:

void APIServerConnection::SendXML ( )
private

Definition at line 389 of file APIServerConnection.cpp.

References _socket, _xmlData, and Close().

Referenced by ProcessHeaders(), and ProcessPostData().

390 {
391  boost::asio::async_write(_socket, boost::asio::buffer(*_xmlData, _xmlData->size()), boost::asio::transfer_all(), std::bind(&APIServerConnection::Close, shared_from_this()));
392 }
std::tr1::shared_ptr< std::vector< char > > _xmlData
boost::asio::ip::tcp::socket _socket

Here is the call graph for this function:

Here is the caller graph for this function:

boost::asio::ip::tcp::socket & APIServerConnection::socket ( )

Definition at line 76 of file APIServerConnection.cpp.

References _socket.

77 {
78  return _socket;
79 }
boost::asio::ip::tcp::socket _socket
bool APIServerConnection::starts_with ( std::string &  haystack,
const char *const  needle 
)
staticprivate

Definition at line 423 of file APIServerConnection.cpp.

Referenced by ProcessHeaders().

424 {
425  return haystack.substr(0, strlen(needle)).compare(needle) == 0;
426 }

Here is the caller graph for this function:

Member Data Documentation

boost::asio::streambuf APIServerConnection::_buffer
private

Definition at line 68 of file APIServerConnection.h.

Referenced by Process(), and ProcessHeaders().

std::string APIServerConnection::_http_cmd_str
private

Definition at line 65 of file APIServerConnection.h.

Referenced by ProcessHeaders(), and ProcessPostData().

boost::asio::streambuf APIServerConnection::_postBuffer
private

Definition at line 69 of file APIServerConnection.h.

Referenced by ProcessHeaders(), and ProcessPostData().

std::string APIServerConnection::_redirectUrl
private

Definition at line 64 of file APIServerConnection.h.

Referenced by RedirectLocation().

boost::asio::const_buffers_1 APIServerConnection::_responseNoContent = boost::asio::buffer("HTTP/1.0 204 No Content", 23)
staticprivate

Definition at line 75 of file APIServerConnection.h.

Referenced by ProcessPostData().

boost::asio::const_buffers_1 APIServerConnection::_responseNotFound
staticprivate

Definition at line 74 of file APIServerConnection.h.

Referenced by NotFound().

boost::asio::const_buffers_1 APIServerConnection::_responseOK = boost::asio::buffer("HTTP/1.0 200 OK\r\nContent-Type: text/xml\r\n\r\n", 43)
staticprivate

Definition at line 73 of file APIServerConnection.h.

Referenced by ProcessHeaders(), and ProcessPostData().

boost::asio::const_buffers_1 APIServerConnection::_responseRedirectBegin = boost::asio::buffer("HTTP/1.0 301 Moved Permanently\r\nLocation: ", 42)
staticprivate

Definition at line 76 of file APIServerConnection.h.

Referenced by Redirect().

boost::asio::const_buffers_1 APIServerConnection::_responseRedirectEnd = boost::asio::buffer("\r\n\r\n", 4)
staticprivate

Definition at line 77 of file APIServerConnection.h.

Referenced by RedirectFinalize().

std::string APIServerConnection::_service
private

Definition at line 62 of file APIServerConnection.h.

Referenced by ProcessHeaders(), ProcessPostData(), and RedirectLocation().

std::string APIServerConnection::_service_handler
private

Definition at line 63 of file APIServerConnection.h.

Referenced by ProcessHeaders(), and ProcessPostData().

boost::asio::ip::tcp::socket APIServerConnection::_socket
private
std::tr1::shared_ptr<std::vector<char> > APIServerConnection::_xmlData
private

Definition at line 71 of file APIServerConnection.h.

Referenced by ProcessHeaders(), ProcessPostData(), and SendXML().

APICommandCall APIServerConnection::m_apiCommandCall
private

Definition at line 66 of file APIServerConnection.h.

Referenced by ProcessHeaders(), and ProcessPostData().


The documentation for this class was generated from the following files: