/src/SchemeREPL.cpp

http://github.com/digego/extempore · C++ · 226 lines · 164 code · 17 blank · 45 comment · 13 complexity · cd389ce13cb0f210adc57710f1f5b151 MD5 · raw file

  1. /*
  2. * Copyright (c) 2011, Andrew Sorensen
  3. *
  4. * All rights reserved.
  5. *
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions are met:
  9. *
  10. * 1. Redistributions of source code must retain the above copyright notice,
  11. * this list of conditions and the following disclaimer.
  12. *
  13. * 2. Redistributions in binary form must reproduce the above copyright notice,
  14. * this list of conditions and the following disclaimer in the documentation
  15. * and/or other materials provided with the distribution.
  16. *
  17. * Neither the name of the authors nor other contributors may be used to endorse
  18. * or promote products derived from this software without specific prior written
  19. * permission.
  20. *
  21. *
  22. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  23. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  24. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  25. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  26. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  27. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  28. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  29. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  30. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  31. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  32. * POSSIBILITY OF SUCH DAMAGE.
  33. *
  34. */
  35. #include "SchemeREPL.h"
  36. #include "EXTMutex.h"
  37. #include "UNIV.h"
  38. #include <stdio.h> /* Basic I/O routines */
  39. #ifdef _WIN32
  40. // nothing
  41. #else
  42. #include <sys/types.h> /* standard system types */
  43. #include <netinet/in.h> /* Internet address structures */
  44. #include <netinet/tcp.h> /* for define of TCP_NODELAY Nagles Algorithm*/
  45. #include <sys/socket.h> /* socket interface functions */
  46. #include <netdb.h> /* host to IP resolution */
  47. #endif
  48. #ifndef _WIN32
  49. #include <unistd.h>
  50. #endif
  51. #include <iostream>
  52. #include <sstream>
  53. #include <string.h>
  54. #include <errno.h>
  55. namespace extemp {
  56. std::unordered_map<std::string, SchemeREPL*> SchemeREPL::sm_repls;
  57. SchemeREPL::SchemeREPL(const std::string& Title, SchemeProcess* Process): m_title(Title), m_process(Process),
  58. m_serverSocket(0), m_connected(false), m_active(true), m_writeLock("repl_lock")
  59. {
  60. ascii_info();
  61. printf("INFO:");
  62. ascii_default();
  63. std::cout << " starting " << m_title << " process..." << std::endl;
  64. m_writeLock.init();
  65. sm_repls[m_title] = this;
  66. }
  67. void SchemeREPL::writeString(std::string&& String)
  68. {
  69. if (!m_serverSocket) {
  70. return;
  71. }
  72. EXTMutex::ScopedLock lock(m_writeLock);
  73. String.push_back('\r');
  74. String.push_back('\n');
  75. int length = String.length();
  76. const char* b = String.c_str();
  77. while (true) {
  78. int lth = (length > 1024) ? 1024 : length;
  79. #ifdef _WIN32
  80. int chars_written = m_serverSocket->write_some(std::experimental::net::buffer(b, lth));
  81. #else
  82. int chars_written = write(m_serverSocket, b, lth);
  83. #endif
  84. if (chars_written != lth) {
  85. printf("There was an error sending this expression to the interpreter. Check for non-ascii characters in your code.\n");
  86. }
  87. length -= lth;
  88. if (length < 1) {
  89. return;
  90. }
  91. b += lth;
  92. }
  93. }
  94. bool SchemeREPL::connectToProcessAtHostname(const std::string& hostname, int port)
  95. {
  96. if (m_connected) {
  97. return false;
  98. }
  99. int rc;
  100. // this whole "trying to connect" print-out is just confusing to newcomers
  101. // I'd delete it, but SB likes to leave these comments in :)
  102. // printf("Trying to connect to ");
  103. // printf("'%s'",hostname.c_str());
  104. // printf(" on port ");
  105. // printf("%d\n",port);
  106. /* Address resolution stage */
  107. #ifdef _WIN32
  108. std::experimental::net::io_context context;
  109. std::experimental::net::ip::tcp::resolver resolver(context);
  110. std::stringstream ss;
  111. ss << port;
  112. std::experimental::net::ip::tcp::resolver::results_type res = resolver.resolve(std::experimental::net::ip::tcp::v4(), hostname, ss.str());
  113. auto iter = res.begin();
  114. auto end = res.end();
  115. std::experimental::net::ip::tcp::endpoint ep = *iter;
  116. //std::cout << "resolved: " << ep << std::endl << std::flush;
  117. if(iter == end) {
  118. #else
  119. struct sockaddr_in sa;
  120. struct hostent* hen; /* host-to-IP translation */
  121. hen = gethostbyname(hostname.c_str());
  122. if (!hen) {
  123. #endif
  124. ascii_error();
  125. printf("Could not resolve host name\n");
  126. ascii_default();
  127. return false;
  128. }
  129. // wait for main server to start up first time out of the gates.
  130. #ifdef _WIN32
  131. Sleep(1000);
  132. #else
  133. sleep(1);
  134. #endif
  135. #ifdef _WIN32
  136. m_serverIoService = new std::experimental::net::io_context;
  137. try {
  138. m_serverSocket = new std::experimental::net::ip::tcp::socket(*m_serverIoService);
  139. m_serverSocket->open(std::experimental::net::ip::tcp::v4());
  140. m_serverSocket->connect(ep);
  141. } catch(std::exception& e){
  142. ascii_error();
  143. std::cout << "Connection Error:" << e.what() << std::endl;
  144. ascii_default();
  145. return false;
  146. }
  147. rc = m_serverSocket->read_some(std::experimental::net::buffer(m_buf, sizeof(m_buf)));
  148. #else
  149. memset(&sa, 0, sizeof(sa));
  150. sa.sin_family = AF_INET;
  151. sa.sin_port = htons(port);
  152. memcpy(&sa.sin_addr.s_addr, hen->h_addr_list[0], hen->h_length);
  153. m_serverSocket = socket(AF_INET, SOCK_STREAM, 0);
  154. if (m_serverSocket < 0) {
  155. ascii_error();
  156. printf("Socket Connection Failed\n");
  157. ascii_default();
  158. return false;
  159. }
  160. int flag = 1;
  161. int result = setsockopt(m_serverSocket, /* socket affected */
  162. IPPROTO_TCP, /* set option at TCP level */
  163. TCP_NODELAY, /* name of option */
  164. (char *) &flag, /* the cast is historical cruft */
  165. sizeof(int)); /* length of option value */
  166. if (result < 0) {
  167. printf("error turning off TCP Nagle ALGO\n");
  168. }
  169. rc = connect(m_serverSocket, (struct sockaddr *)&sa, sizeof(sa));
  170. if (rc) {
  171. ascii_error();
  172. printf("Connection error:%d\n",errno);
  173. ascii_default();
  174. return false;
  175. }
  176. //should now be connected
  177. rc = read(m_serverSocket, m_buf, sizeof(m_buf));
  178. #endif
  179. if (!rc) {
  180. this->closeREPL();
  181. ascii_warning();
  182. printf("WARN:");
  183. ascii_default();
  184. std::cout << " could not connect " << m_title << " process to port " << port << " (port is in use by another process)" << std::endl;
  185. return false;
  186. }
  187. ascii_info();
  188. printf("INFO:");
  189. ascii_default();
  190. std::cout << " client: connected to server " << m_title << " process at " << hostname << ":" << port << std::endl;
  191. m_connected = true;
  192. return true;
  193. }
  194. void SchemeREPL::closeREPL()
  195. {
  196. m_active = false;
  197. #ifdef _WIN32
  198. m_serverSocket->close();
  199. delete m_serverSocket;
  200. delete m_serverIoService;
  201. m_serverIoService = 0;
  202. #else
  203. shutdown(m_serverSocket, SHUT_RDWR);
  204. close(m_serverSocket);
  205. #endif
  206. m_serverSocket = 0;
  207. m_connected = false;
  208. }
  209. }