PageRenderTime 21ms CodeModel.GetById 2ms app.highlight 16ms RepoModel.GetById 1ms app.codeStats 0ms

/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
 36#include "SchemeREPL.h"
 37#include "EXTMutex.h"
 38#include "UNIV.h"
 39
 40#include <stdio.h>         /* Basic I/O routines          */
 41
 42#ifdef _WIN32
 43// nothing
 44#else
 45#include <sys/types.h>     /* standard system types       */
 46#include <netinet/in.h>    /* Internet address structures */
 47#include <netinet/tcp.h>   /* for define of TCP_NODELAY  Nagles Algorithm*/
 48#include <sys/socket.h>    /* socket interface functions  */
 49#include <netdb.h>         /* host to IP resolution       */
 50#endif
 51
 52#ifndef _WIN32
 53#include <unistd.h>
 54#endif
 55#include <iostream>
 56#include <sstream>
 57#include <string.h>
 58#include <errno.h>
 59
 60namespace extemp {
 61
 62std::unordered_map<std::string, SchemeREPL*> SchemeREPL::sm_repls;
 63
 64SchemeREPL::SchemeREPL(const std::string& Title, SchemeProcess* Process): m_title(Title), m_process(Process),
 65        m_serverSocket(0), m_connected(false), m_active(true), m_writeLock("repl_lock")
 66{
 67    ascii_info();
 68	printf("INFO:");
 69	ascii_default();
 70	std::cout << " starting " << m_title << " process..." << std::endl;
 71    m_writeLock.init();
 72    sm_repls[m_title] = this;
 73}
 74
 75void SchemeREPL::writeString(std::string&& String)
 76{
 77    if (!m_serverSocket) {
 78        return;
 79    }
 80    EXTMutex::ScopedLock lock(m_writeLock);
 81    String.push_back('\r');
 82    String.push_back('\n');
 83    int length = String.length();
 84    const char* b = String.c_str();
 85    while (true) {
 86        int lth = (length > 1024) ? 1024 : length;
 87#ifdef _WIN32
 88        int chars_written = m_serverSocket->write_some(std::experimental::net::buffer(b, lth));
 89#else
 90        int chars_written = write(m_serverSocket, b, lth);
 91#endif
 92        if (chars_written != lth) {
 93            printf("There was an error sending this expression to the interpreter. Check for non-ascii characters in your code.\n");
 94        }
 95        length -= lth;
 96        if (length < 1) {
 97            return;
 98        }
 99        b += lth;
100    }
101}
102
103bool SchemeREPL::connectToProcessAtHostname(const std::string& hostname, int port)
104{
105    if (m_connected) {
106        return false;
107    }
108    int rc;
109	// this whole "trying to connect" print-out is just confusing to newcomers
110	// I'd delete it, but SB likes to leave these comments in :)
111
112    // printf("Trying to connect to ");
113    // printf("'%s'",hostname.c_str());
114    // printf(" on port ");
115    // printf("%d\n",port);
116
117    /* Address resolution stage */
118
119#ifdef _WIN32
120    std::experimental::net::io_context context;
121    std::experimental::net::ip::tcp::resolver resolver(context);
122    std::stringstream ss;
123    ss << port;
124	std::experimental::net::ip::tcp::resolver::results_type res = resolver.resolve(std::experimental::net::ip::tcp::v4(), hostname, ss.str());
125	auto iter = res.begin();
126	auto end = res.end();
127    std::experimental::net::ip::tcp::endpoint ep = *iter;
128    //std::cout << "resolved: " << ep << std::endl << std::flush;
129    if(iter == end) {
130#else
131	struct sockaddr_in sa;
132	struct hostent* hen; /* host-to-IP translation */
133    hen = gethostbyname(hostname.c_str());
134    if (!hen) {
135#endif
136        ascii_error();
137        printf("Could not resolve host name\n");
138        ascii_default();
139        return false;
140    }
141    // wait for main server to start up first time out of the gates.
142#ifdef _WIN32
143    Sleep(1000);
144#else
145    sleep(1);
146#endif
147
148#ifdef _WIN32
149    m_serverIoService = new std::experimental::net::io_context;
150    try {
151        m_serverSocket = new std::experimental::net::ip::tcp::socket(*m_serverIoService);
152        m_serverSocket->open(std::experimental::net::ip::tcp::v4());
153        m_serverSocket->connect(ep);
154    } catch(std::exception& e){
155        ascii_error();
156        std::cout << "Connection Error:" << e.what() << std::endl;
157        ascii_default();
158        return false;
159    }
160    rc = m_serverSocket->read_some(std::experimental::net::buffer(m_buf, sizeof(m_buf)));
161#else
162    memset(&sa, 0, sizeof(sa));
163    sa.sin_family = AF_INET;
164    sa.sin_port = htons(port);
165    memcpy(&sa.sin_addr.s_addr, hen->h_addr_list[0], hen->h_length);
166    m_serverSocket = socket(AF_INET, SOCK_STREAM, 0);
167
168    if (m_serverSocket < 0) {
169        ascii_error();
170        printf("Socket Connection Failed\n");
171        ascii_default();
172        return false;
173    }
174    int flag = 1;
175    int result = setsockopt(m_serverSocket,            /* socket affected */
176                            IPPROTO_TCP,     /* set option at TCP level */
177                            TCP_NODELAY,     /* name of option */
178                            (char *) &flag,  /* the cast is historical cruft */
179                            sizeof(int));    /* length of option value */
180    if (result < 0) {
181        printf("error turning off TCP Nagle ALGO\n");
182    }
183    rc = connect(m_serverSocket, (struct sockaddr *)&sa, sizeof(sa));
184    if (rc) {
185        ascii_error();
186        printf("Connection error:%d\n",errno);
187        ascii_default();
188        return false;
189    }
190
191    //should now be connected
192    rc = read(m_serverSocket, m_buf, sizeof(m_buf));
193#endif
194    if (!rc) {
195        this->closeREPL();
196		ascii_warning();
197		printf("WARN:");
198		ascii_default();
199		std::cout << " could not connect " << m_title << " process to port " << port << " (port is in use by another process)" << std::endl;
200        return false;
201    }
202	ascii_info();
203    printf("INFO:");
204	ascii_default();
205	std::cout << " client: connected to server " << m_title << " process at " << hostname << ":" << port << std::endl;
206    m_connected = true;
207    return true;
208}
209
210void SchemeREPL::closeREPL()
211{
212    m_active = false;
213#ifdef _WIN32
214    m_serverSocket->close();
215    delete m_serverSocket;
216    delete m_serverIoService;
217    m_serverIoService = 0;
218#else
219    shutdown(m_serverSocket, SHUT_RDWR);
220    close(m_serverSocket);
221#endif
222    m_serverSocket = 0;
223    m_connected = false;
224}
225
226}