| /* 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, name); |
| 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]; |
| } |
| |