Logo Search packages:      
Sourcecode: gsoap version File versions

dimeserver.cpp

/*    dimeserver.cpp

      Example streaming DIME server. Supports three methods:

      putData stores multiple data sets on the server and returns named keys
            to each data set. The keys are unique and provide access to
            the data. Once data is stored, it cannot be removed.
      getData retrieves data sets given a set of named keys.
      getImage is an example file-based image retrieval method

      Data is stored in the current directory or the directory specified
      by the TMPDIR environment variable.

      Runs as CGI (not multi-threaded) or multi-threaded stand-alone
      Web service

      Copyright (C) 2000-2003 Robert A. van Engelen, Genivia, Inc.
      All Rights Reserved.

      NOTE: THE SERVER WILL ONLY SEND FILES THAT ARE IN THE CURRENT DIR FOR
      SECURITY REASONS. HOWEVER, THE AUTHOR IS NOT RESPONSIBLE FOR ANY DAMAGES
      THAT MAY RESULT FROM THE USE OF THIS PROGRAM.
      
      Usage:
      For stand-alone Web service functionality, run from the command line
      with port number as command line argument, e.g.
      > dimeserver 8085 &
      Use port 80 to run as HTTP Web server accessible over the Web
      Change the 'endpoint' in 'dimeclient.cpp' to
      endpoint="http://machine:8085"
      where 'machine' is the host name of the machine on which the service
      runs, or 'localhost' if the server runs on the same machine. Be careful
      to run the client in a separate directory from the server. Otherwise,
      the client and server may interfere in their file access.

      The service is multi-threaded. Multi-threading is not required, but can
      improve QoS. Remove the pthread code to obtain a non-multi-threaded
      service.

      Unix/Linux: add a sigpipe handler to avoid broken pipes with
      stand-alone server.

*/


#include "soapH.h"
#include "dime.nsmap"
#include <pthread.h>    // use Pthreads
#include <sys/stat.h>   // use fstat() for streaming DIME
#include <assert.h>

#define BACKLOG         (100) // max request backlog
#define MAX_FILE_SIZE   (10000)     // max file size when buffering file for HTTP 1.0 and file size cannot be determined

////////////////////////////////////////////////////////////////////////////////
//
//    Forward decls
//
////////////////////////////////////////////////////////////////////////////////

static void *process_request(void*);
static int getdata(struct soap*, const char*, ns__Data&);
static void saveData(ns__Data&, const char*);
static void sigpipe_handle(int);

////////////////////////////////////////////////////////////////////////////////
//
//    Streaming DIME attachment content handlers
//
////////////////////////////////////////////////////////////////////////////////

static void *dime_read_open(struct soap*, void*, const char*, const char*, const char*);
static void dime_read_close(struct soap*, void*);
static size_t dime_read(struct soap*, void*, char*, size_t);
static void *dime_write_open(struct soap*, const char*, const char*, const char*);
static void dime_write_close(struct soap*, void*);
static int dime_write(struct soap*, void*, const char*, size_t);

////////////////////////////////////////////////////////////////////////////////
//
//    Data for streaming DIME write handler
//
////////////////////////////////////////////////////////////////////////////////

struct dime_write_handle
{ char *name;     // file name
  FILE *fd; // file fd
};

////////////////////////////////////////////////////////////////////////////////
//
//    Static data
//
////////////////////////////////////////////////////////////////////////////////

static char *TMPDIR = ".";

////////////////////////////////////////////////////////////////////////////////
//
//    Main
//
////////////////////////////////////////////////////////////////////////////////

int main(int argc, char **argv)
{ struct soap soap;
  char *s = getenv("TMPDIR");
  if (s)
    TMPDIR = s;
  // use HTTP chunking when possible
  // chunking allows streaming of DIME content without requiring DIME attachment size to be set
  // DIME attachments can be streamed without chunking only if the attachment size is set
  soap_init1(&soap, SOAP_IO_KEEPALIVE | SOAP_IO_CHUNK);
  // set DIME callbacks
  soap.fdimereadopen = dime_read_open;
  soap.fdimereadclose = dime_read_close;
  soap.fdimeread = dime_read;
  soap.fdimewriteopen = dime_write_open;
  soap.fdimewriteclose = dime_write_close;
  soap.fdimewrite = dime_write;
  if (argc < 2)
  { // no args: assume this is a CGI application
    // serve request
    soap_serve(&soap);
    // cleanup class instances
    soap_destroy(&soap);
    // cleanup
    soap_end(&soap);
  }
  else
  { struct soap *tsoap;
    pthread_t tid;
    int port;
    int m, s, i;
    // Unix SIGPIPE, this is OS dependent:
    // soap.accept_flags = SO_NOSIGPIPE;  // some systems like this
    // soap.socket_flags = MSG_NOSIGNAL;  // others need this
    // signal(SIGPIPE, sigpipe_handle);         // or a sigpipe handler (portable)
    // port is first command line argument
    port = atoi(argv[1]);
    // bind to current host and specified port
    m = soap_bind(&soap, NULL, port, BACKLOG);
    // if we could not bind, exit
    if (m < 0)
    { soap_print_fault(&soap, stderr);
      exit(1);
    }
    fprintf(stderr, "Socket connection successful %d\n", m);
    // die after 24 hrs waiting for activity on port
    soap.accept_timeout = 24*60*60;
    // IO timeouts
    soap.send_timeout = 30;
    soap.recv_timeout = 30;
    // loop through requests
    for (i = 1; ; i++)
    { // accept request
      s = soap_accept(&soap);
      // if timeout or error, report
      if (s < 0)
      { if (soap.errnum)
          soap_print_fault(&soap, stderr);
      else
          fprintf(stderr, "Server timed out\n");
      break;
      }
      fprintf(stderr, "Thread %d accepts socket %d connection from IP %d.%d.%d.%d\n", i, s, (int)(soap.ip>>24)&0xFF, (int)(soap.ip>>16)&0xFF, (int)(soap.ip>>8)&0xFF, (int)soap.ip&0xFF);
      // copy soap environment and spawn thread
      tsoap = soap_copy(&soap);
      pthread_create(&tid, NULL, (void*(*)(void*))process_request, (void*)tsoap);
    }
  }
  // detach
  soap_done(&soap);
  return 0;
}

void *process_request(void *soap)
{ pthread_detach(pthread_self());
  // serve request (or multiple requests with keep-alive enabled)
  soap_serve((struct soap*)soap);
  // cleanup class instances
  soap_destroy((struct soap*)soap);
  // cleanup
  soap_end((struct soap*)soap);
  // detach thread's copy of soap environment
  soap_done((struct soap*)soap);
  // free soap environment
  free(soap);
  return NULL;
}

////////////////////////////////////////////////////////////////////////////////
//
//    Server methods
//
////////////////////////////////////////////////////////////////////////////////

int ns__putData(struct soap *soap, arrayOfData *data, arrayOfName *names)
{ // gSOAP switches to SOAP_IO_STORE when SOAP_IO_CHUNK (HTTP chunking) is not supported by the client
  // Since it is undesirable to use SOAP_IO_STORE with streaming, we reset it to SOAP_IO_BUFFER
  if ((soap->omode & SOAP_IO) == SOAP_IO_STORE)
    soap->omode = (soap->omode & ~SOAP_IO) | SOAP_IO_BUFFER;
  // return name (key) for each data item
  names->resize(data->size());
  for (int i = 0; i < data->size(); i++)
  { char *s, *name;
    // the id field is set when DIME was used so the dime_write callbacks already saved the file
    if ((*data)[i].id)
    { assert((*data)[i].__ptr);
      name = ((struct dime_write_handle*)(*data)[i].__ptr)->name;
    }
    else
    { name = tempnam(TMPDIR, "data");
      fprintf(stderr, "Saving file %s\n", name);
      saveData((*data)[i], name);
    }
    s = strrchr(name, '/');
    if (!s)
      s = strrchr(name, '\\');
    if (!s)
      s = name;
    else
      s++;
    (*names)[i] = soap_strdup(soap, s);
    if (!(*data)[i].id)
    { // free data alloced with tempnam()
      free(name);
    }
  }
  return SOAP_OK;
}

int ns__getData(struct soap *soap, arrayOfName *names, arrayOfData *data)
{ // gSOAP switches to SOAP_IO_STORE when SOAP_IO_CHUNK (HTTP chunking) is not supported by the client.
  // Since it is undesirable to use SOAP_IO_STORE, we reset it to SOAP_IO_BUFFER
  // Important: DIME attachments MAY be transmitted in a permuted order, so we must be careful with streaming data to files at the client side. Therefore, we save the file name in the options field.
  if ((soap->omode & SOAP_IO) == SOAP_IO_STORE)
    soap->omode = (soap->omode & ~SOAP_IO) | SOAP_IO_BUFFER;
  if (!names)
    return soap_sender_fault(soap, "Names required", NULL);
  data->resize(names->size());
  for (int i = 0; i < names->__size; i++)
  { if (!(*names)[i])
      return soap_sender_fault(soap, "Name required", NULL);
    if (getdata(soap, (*names)[i], (*data)[i]))
      return soap_sender_fault(soap, "Access denied", NULL);
  }
  return SOAP_OK;
}

int ns__getImage(struct soap *soap, char *name, ns__Data& image)
{ if (!name)
    return soap_sender_fault(soap, "Name required", NULL);
  if (getdata(soap, name, image))
    return soap_sender_fault(soap, "Access denied", NULL);
  image.type = "image/jpeg";
  image.options = soap_dime_option(soap, 0, "My picture");
  return SOAP_OK;
}

////////////////////////////////////////////////////////////////////////////////
//
//    Helper functions
//
////////////////////////////////////////////////////////////////////////////////

static int getdata(struct soap *soap, const char *name, ns__Data& data)
{ struct stat sb;
  FILE *fd = NULL;
  if (!strchr(name, '/') && !strchr(name, '\\') && !strchr(name, ':'))
  { char *s = (char*)soap_malloc(soap, strlen(TMPDIR) + strlen(name) + 2);
    strcpy(s, TMPDIR);
    strcat(s, "/");
    strcpy(s, name);
    fd = fopen(s, "rb");
  }
  if (!fd)
    return SOAP_EOF;
  if ((soap->omode & SOAP_IO) == SOAP_IO_CHUNK) // chunked response is possible
  { data.__ptr = (unsigned char*)fd; // must set to non-NULL (this is our fd handle which we need in the callbacks)
    data.__size = 0; // zero size streams data with HTTP chunking
  }
  else if (!fstat(fileno(fd), &sb) && sb.st_size > 0)
  { // since we can get the length of the file, we can stream it
    data.__ptr = (unsigned char*)fd; // must set to non-NULL (this is our fd handle which we need in the callbacks)
    data.__size = sb.st_size;
  }
  else // we can't use HTTP chunking and we don't know the size, so buffer it
  { int i;
    data.__ptr = (unsigned char*)soap_malloc(soap, MAX_FILE_SIZE);
    for (i = 0; i < MAX_FILE_SIZE; i++)
    { int c;
      if ((c = fgetc(fd)) == EOF)
        break;
      data.__ptr[i] = c;
    }
    fclose(fd);
    data.__size = i;
  }
  data.type = ""; // specify non-NULL id or type to enable DIME
  data.options = soap_dime_option(soap, 0, name);
  return SOAP_OK;
}

static void saveData(ns__Data& data, const char *name)
{ char *buf = (char*)data.__ptr;
  int len = data.__size;
  FILE *fd = fopen(name, "wb");
  if (!fd)
  { fprintf(stderr, "Cannot save file %s\n", name);
    return;
  }
  while (len)
  { size_t nwritten = fwrite(buf, 1, len, fd);
    if (!nwritten)
    { fprintf(stderr, "Cannot write to %s\n", name);
      return;
    }
    len -= nwritten;
    buf += nwritten;
  }
}

static void sigpipe_handle(int x) { }

////////////////////////////////////////////////////////////////////////////////
//
//    Streaming DIME attachment content handlers
//
////////////////////////////////////////////////////////////////////////////////

static void *dime_read_open(struct soap *soap, void *handle, const char *id, const char *type, const char *options)
{ // we should return NULL without setting soap->error if we don't want to use the streaming callback for this DIME attachment. The handle contains the non-NULL __ptr field value which should have been set in the application.
  // return value of this function will be passed on to the fdimeread and fdimereadclose callbacks. The return value will not affect the __ptr field.
  return handle;
}

static void dime_read_close(struct soap *soap, void *handle)
{ fclose((FILE*)handle);
}

static size_t dime_read(struct soap *soap, void *handle, char *buf, size_t len)
{ return fread(buf, 1, len, (FILE*)handle);
}

static void *dime_write_open(struct soap *soap, const char *id, const char *type, const char *options)
{ // we can return NULL without setting soap->error if we don't want to use the streaming callback for this DIME attachment
  struct dime_write_handle *handle = (struct dime_write_handle*)soap_malloc(soap, sizeof(struct dime_write_handle));
  if (!handle)
  { soap->error = SOAP_EOM;
    return NULL;
  }
  char *name = tempnam(TMPDIR, "data");
  fprintf(stderr, "Saving file %s\n", name);
  handle->name = soap_strdup(soap, name);
  free(name);
  handle->fd = fopen(handle->name, "wb");
  if (!handle->fd)
  { soap->error = SOAP_EOF; // could not open file for writing
    soap->errnum = errno; // get reason
    return NULL;
  }
  return (void*)handle;
}

static void dime_write_close(struct soap *soap, void *handle)
{ fclose(((struct dime_write_handle*)handle)->fd);
}

static int dime_write(struct soap *soap, void *handle, const char *buf, size_t len)
{ while (len)
  { size_t nwritten = fwrite(buf, 1, len, ((struct dime_write_handle*)handle)->fd);
    if (!nwritten)
    { soap->errnum = errno; // get reason
      return SOAP_EOF;
    }
    len -= nwritten;
    buf += nwritten;
  }
  return SOAP_OK;
}

////////////////////////////////////////////////////////////////////////////////
//
//    ns__Data class
//
////////////////////////////////////////////////////////////////////////////////

ns__Data::ns__Data()
{ __ptr = NULL;
  __size = 0;
  id = NULL;
  type = NULL;
  options = NULL;
  soap = NULL;
}

////////////////////////////////////////////////////////////////////////////////
//
//    arrayOfData class
//
////////////////////////////////////////////////////////////////////////////////

arrayOfData::arrayOfData()
{ __ptr = NULL;
  __size = 0;
  soap = NULL;
}

arrayOfData::arrayOfData(struct soap *soap, int n)
{ __ptr = NULL;
  __size = 0;
  this->soap = soap;
  resize(n);
}

arrayOfData::~arrayOfData()
{ resize(0);
}

int arrayOfData::size()
{ return __size;
}

void arrayOfData::resize(int n)
{ if (__ptr)
  { if (soap) // if created by soap environment
      soap_delete(soap, __ptr); // then delete
    else
      delete[] __ptr;
  }
  __size = n;
  if (n <= 0)
    __ptr = NULL;
  else if (soap)
    __ptr = soap_new_ns__Data(soap, n);
  else
    __ptr = new ns__Data[n];
}

ns__Data& arrayOfData::operator[](int i) const
{ assert(__ptr && i >= 0 && i < __size);
  return __ptr[i];
}

////////////////////////////////////////////////////////////////////////////////
//
//    arrayOfName class
//
////////////////////////////////////////////////////////////////////////////////

arrayOfName::arrayOfName()
{ __ptr = NULL;
  __size = 0;
  soap = NULL;
}

arrayOfName::arrayOfName(struct soap *soap, int n)
{ __ptr = NULL;
  __size = 0;
  this->soap = soap;
  resize(n);
}

arrayOfName::~arrayOfName()
{ resize(0);
}

int arrayOfName::size()
{ return __size;
}

void arrayOfName::resize(int n)
{ if (__ptr)
  { if (soap) // if created by soap environment
      soap_delete(soap, __ptr); // then delete
    else
      free(__ptr);
  }
  __size = n;
  if (n <= 0)
    __ptr = NULL;
  else
  { if (soap)
      __ptr = (char**)soap_malloc(soap, sizeof(char*) * n);
    else
      __ptr = (char**)malloc(sizeof(char*) * n);
    memset(__ptr, 0, n);
  }
}

char*& arrayOfName::operator[](int i) const
{ assert(__ptr && i >= 0 && i < __size);
  return __ptr[i];
}


Generated by  Doxygen 1.6.0   Back to index