text: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements. See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership. The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License. You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing,
   * software distributed under the License is distributed on an
   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   * KIND, either express or implied. See the License for the
   * specific language governing permissions and limitations
   * under the License.
   */
  
  #include <errno.h>
  #include <netdb.h>
  #include <stdlib.h>
  #include <string.h>
  #include <unistd.h>
  #include <sys/socket.h>
  #include <sys/un.h>
  #include <netinet/in.h>
  
  #include <thrift/c_glib/thrift.h>
  #include <thrift/c_glib/transport/thrift_transport.h>
  #include <thrift/c_glib/transport/thrift_socket.h>
  
  /* object properties */
  enum _ThriftSocketProperties
  {
    PROP_0,
    PROP_THRIFT_SOCKET_HOSTNAME,
    PROP_THRIFT_SOCKET_PORT,
    PROP_THRIFT_SOCKET_PATH
  };
  
  G_DEFINE_TYPE(ThriftSocket, thrift_socket, THRIFT_TYPE_TRANSPORT)
  
  /* implements thrift_transport_is_open */
  gboolean
  thrift_socket_is_open (ThriftTransport *transport)
  {
    ThriftSocket *socket = THRIFT_SOCKET (transport);
    return socket->sd != THRIFT_INVALID_SOCKET;
  }
  
  /* overrides thrift_transport_peek */
  gboolean
  thrift_socket_peek (ThriftTransport *transport, GError **error)
  {
    gboolean result = FALSE;
    guint8 buf;
    int r;
    int errno_copy;
  
    ThriftSocket *socket = THRIFT_SOCKET (transport);
  
    if (thrift_socket_is_open (transport))
    {
      r = recv (socket->sd, &buf, 1, MSG_PEEK);
      if (r == -1)
      {
        errno_copy = errno;
  
        #if defined __FreeBSD__ || defined __MACH__
        /* FreeBSD returns -1 and ECONNRESET if the socket was closed by the other
           side */
        if (errno_copy == ECONNRESET)
        {
          thrift_socket_close (transport, error);
        }
        else
        {
        #endif
  
        g_set_error (error,
                     THRIFT_TRANSPORT_ERROR,
                     THRIFT_TRANSPORT_ERROR_SOCKET,
                     "failed to peek at socket - %s",
                     strerror (errno_copy));
  
        #if defined __FreeBSD__ || defined __MACH__
        }
        #endif
      }
      else if (r > 0)
      {
        result = TRUE;
      }
    }
  
    return result;
  }
  
  
  /* implements thrift_transport_close */
  gboolean
  thrift_socket_close (ThriftTransport *transport, GError **error)
  {
    ThriftSocket *socket = THRIFT_SOCKET (transport);
  
    if (close (socket->sd) == -1)
    {
      g_set_error (error, THRIFT_TRANSPORT_ERROR, THRIFT_TRANSPORT_ERROR_CLOSE,
                   "unable to close socket - %s",
                   strerror(errno));
      return FALSE;
    }
  
    socket->sd = THRIFT_INVALID_SOCKET;
    return TRUE;
  }
  
  /* implements thrift_transport_open */
  gboolean
  thrift_socket_open (ThriftTransport *transport, GError **error)
  {
    struct hostent *hp = NULL;
    struct sockaddr_in pin;
    int err;
  #if defined(HAVE_GETHOSTBYNAME_R)
    struct hostent he;
    char buf[1024];
  #endif
  
    ThriftSocket *tsocket = THRIFT_SOCKET (transport);
    g_return_val_if_fail (tsocket->sd == THRIFT_INVALID_SOCKET, FALSE);
    
    if (tsocket->path) {
      /* create a socket structure */
      struct sockaddr_un pin;
      memset (&pin, 0, sizeof(pin));
      pin.sun_family = AF_UNIX;
      memcpy(pin.sun_path, tsocket->path, strlen(tsocket->path) + 1);
  
      /* create the socket */
      if ((tsocket->sd = socket (PF_UNIX, SOCK_STREAM, 0)) == -1)
      {
        g_set_error (error, THRIFT_TRANSPORT_ERROR, THRIFT_TRANSPORT_ERROR_SOCKET,
                     "failed to create socket for path %s: - %s",
                     tsocket->path,
                     strerror(errno));
        return FALSE;
      }
  
      /* open a connection */
      if (connect (tsocket->sd, (struct sockaddr *) &pin, sizeof(pin)) == -1)
      {
          thrift_socket_close(tsocket, NULL);
          g_set_error (error, THRIFT_TRANSPORT_ERROR, THRIFT_TRANSPORT_ERROR_CONNECT,
                     "failed to connect to path %s: - %s",
                     tsocket->path, strerror(errno));
        return FALSE;
      }
      return TRUE;
    }
  
    /* lookup the destination host */
  #if defined(HAVE_GETHOSTBYNAME_R)
    if (gethostbyname_r (tsocket->hostname, &he, buf, 1024, &hp, &err) != 0 || hp == NULL)
  #else
    if ((hp = gethostbyname (tsocket->hostname)) == NULL && (err = h_errno))
  #endif
    {
      /* host lookup failed, bail out with an error */
      g_set_error (error, THRIFT_TRANSPORT_ERROR, THRIFT_TRANSPORT_ERROR_HOST,
                   "host lookup failed for %s:%d - %s",
                   tsocket->hostname, tsocket->port,
                   hstrerror (err));
      return FALSE;
    }
  
    /* create a socket structure */
    memset (&pin, 0, sizeof(pin));
    pin.sin_family = AF_INET;
    pin.sin_addr.s_addr = ((struct in_addr *) (hp->h_addr_list[0]))->s_addr;
    pin.sin_port = htons (tsocket->port);
  
    /* create the socket */
    if ((tsocket->sd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
    {
      g_set_error (error, THRIFT_TRANSPORT_ERROR, THRIFT_TRANSPORT_ERROR_SOCKET,
                   "failed to create socket for host %s:%d - %s",
                   tsocket->hostname, tsocket->port,
                   strerror(errno));
      return FALSE;
    }
  
    /* open a connection */
    if (connect (tsocket->sd, (struct sockaddr *) &pin, sizeof(pin)) == -1)
    {
        thrift_socket_close(transport, NULL);
        g_set_error (error, THRIFT_TRANSPORT_ERROR, THRIFT_TRANSPORT_ERROR_CONNECT,
                   "failed to connect to host %s:%d - %s",
                   tsocket->hostname, tsocket->port, strerror(errno));
      return FALSE;
    }
  
    return TRUE;
  }
  
  
  /* implements thrift_transport_read */
  gint32
  thrift_socket_read (ThriftTransport *transport, gpointer buf,
                      guint32 len, GError **error)
  {
    gint ret = 0;
    guint got = 0;
  
    ThriftSocket *socket = THRIFT_SOCKET (transport);
  
    while (got < len)
    {
      ret = recv (socket->sd, (guint8 *)buf + got, len-got, 0);
      if (ret <= 0)
      {
        g_set_error (error, THRIFT_TRANSPORT_ERROR,
                     THRIFT_TRANSPORT_ERROR_RECEIVE,
                     "failed to read %d bytes - %s", len, strerror(errno));
        return -1;
      }
      got += ret;
    }
  
    return got;
  }
  
  /* implements thrift_transport_read_end
   * called when write is complete.  nothing to do on our end. */
  gboolean
  thrift_socket_read_end (ThriftTransport *transport, GError **error)
  {
    /* satisfy -Wall */
    THRIFT_UNUSED_VAR (transport);
    THRIFT_UNUSED_VAR (error);
    return TRUE;
  }
  
  /* implements thrift_transport_write */
  gboolean
  thrift_socket_write (ThriftTransport *transport, const gpointer buf,
                       const guint32 len, GError **error)
  {
    gint ret = 0;
    guint sent = 0;
  
    ThriftSocket *socket = THRIFT_SOCKET (transport);
    g_return_val_if_fail (socket->sd != THRIFT_INVALID_SOCKET, FALSE);
  
    while (sent < len)
    {
      ret = send (socket->sd, (guint8 *)buf + sent, len - sent, 0);
      if (ret < 0)
      {
        g_set_error (error, THRIFT_TRANSPORT_ERROR,
                     THRIFT_TRANSPORT_ERROR_SEND,
                     "failed to send %d bytes - %s", len, strerror(errno));
        return FALSE;
      }
      sent += ret;
    }
  
    return TRUE;
  }
  
  /* implements thrift_transport_write_end
   * called when write is complete.  nothing to do on our end. */
  gboolean
  thrift_socket_write_end (ThriftTransport *transport, GError **error)
  {
    /* satisfy -Wall */
    THRIFT_UNUSED_VAR (transport);
    THRIFT_UNUSED_VAR (error);
    return TRUE;
  }
  
  /* implements thrift_transport_flush
   * flush pending data.  since we are not buffered, this is a no-op */
  gboolean
  thrift_socket_flush (ThriftTransport *transport, GError **error)
  {
    /* satisfy -Wall */
    THRIFT_UNUSED_VAR (transport);
    THRIFT_UNUSED_VAR (error);
    return TRUE;
  }
  
  /* initializes the instance */
  static void
  thrift_socket_init (ThriftSocket *socket)
  {
    socket->sd = THRIFT_INVALID_SOCKET;
  }
  
  /* destructor */
  static void
  thrift_socket_finalize (GObject *object)
  {
    ThriftSocket *socket = THRIFT_SOCKET (object);
  
    if (socket->hostname != NULL)
    {
      g_free (socket->hostname);
    }
    socket->hostname = NULL;
    if (socket->path != NULL)
    {
      g_free (socket->path);
    }
  
    if (socket->sd != THRIFT_INVALID_SOCKET)
    {
      close (socket->sd);
    }
    socket->sd = THRIFT_INVALID_SOCKET;
  }
  
  /* property accessor */
  void
  thrift_socket_get_property (GObject *object, guint property_id,
                              GValue *value, GParamSpec *pspec)
  {
    ThriftSocket *socket = THRIFT_SOCKET (object);
  
    THRIFT_UNUSED_VAR (pspec);
  
    switch (property_id)
    {
      case PROP_THRIFT_SOCKET_HOSTNAME:
        g_value_set_string (value, socket->hostname);
        break;
      case PROP_THRIFT_SOCKET_PORT:
        g_value_set_uint (value, socket->port);
        break;
      case PROP_THRIFT_SOCKET_PATH:
        g_value_set_string (value, socket->path);
        break;
    }
  }
  
  /* property mutator */
  void
  thrift_socket_set_property (GObject *object, guint property_id,
                              const GValue *value, GParamSpec *pspec)
  {
    ThriftSocket *socket = THRIFT_SOCKET (object);
  
    THRIFT_UNUSED_VAR (pspec);
  
    switch (property_id)
    {
      case PROP_THRIFT_SOCKET_HOSTNAME:
        if (socket->hostname) {
          g_free(socket->hostname);
        }
        socket->hostname = g_strdup (g_value_get_string (value));
        break;
      case PROP_THRIFT_SOCKET_PORT:
        socket->port = g_value_get_uint (value);
        break;
      case PROP_THRIFT_SOCKET_PATH:
        if (socket->path) {
          g_free(socket->path);
        }
        socket->path = g_strdup (g_value_get_string (value));
        break;
    }
  }
  
  /* initializes the class */
  static void
  thrift_socket_class_init (ThriftSocketClass *cls)
  {
    ThriftTransportClass *ttc = THRIFT_TRANSPORT_CLASS (cls);
    GObjectClass *gobject_class = G_OBJECT_CLASS (cls);
    GParamSpec *param_spec = NULL;
  
    /* setup accessors and mutators */
    gobject_class->get_property = thrift_socket_get_property;
    gobject_class->set_property = thrift_socket_set_property;
  
    param_spec = g_param_spec_string ("hostname",
                                      "hostname (construct)",
                                      "Set the hostname of the remote host",
                                      "localhost", /* default value */
                                      G_PARAM_CONSTRUCT_ONLY |
                                      G_PARAM_READWRITE);
    g_object_class_install_property (gobject_class, PROP_THRIFT_SOCKET_HOSTNAME,
                                     param_spec);
  
    param_spec = g_param_spec_uint ("port",
                                    "port (construct)",
                                    "Set the port of the remote host",
                                    0u, /* min */
                                    65535u, /* max */
                                    9090, /* default by convention */
                                    G_PARAM_CONSTRUCT_ONLY |
                                    G_PARAM_READWRITE);
    g_object_class_install_property (gobject_class, PROP_THRIFT_SOCKET_PORT,
                                     param_spec);
  
    param_spec = g_param_spec_string ("path",
                                      "path (construct)",
                                      "Set the path of the remote host",
                                      NULL, /* default value */
                                      G_PARAM_CONSTRUCT_ONLY |
                                      G_PARAM_READWRITE);
    g_object_class_install_property (gobject_class, PROP_THRIFT_SOCKET_PATH,
                                     param_spec);
  
    gobject_class->finalize = thrift_socket_finalize;
    ttc->is_open = thrift_socket_is_open;
    ttc->peek = thrift_socket_peek;
    ttc->open = thrift_socket_open;
    ttc->close = thrift_socket_close;
    ttc->read = thrift_socket_read;
    ttc->read_end = thrift_socket_read_end;
    ttc->write = thrift_socket_write;
    ttc->write_end = thrift_socket_write_end;
    ttc->flush = thrift_socket_flush;
  }
  