📄 webserver.c
字号:
/*webserver.cExample stand-alone gSOAP Web server based on the gSOAP HTTP GET plugin.This is a small but fully functional (embedded) Web server for servingstatic and dynamic pages and SOAP/XML responses.--------------------------------------------------------------------------------gSOAP XML Web services toolsCopyright (C) 2001-2004, Robert van Engelen, Genivia, Inc. All Rights Reserved.This software is released under one of the following two licenses:GPL or Genivia's license for commercial use.--------------------------------------------------------------------------------GPL license.This program is free software; you can redistribute it and/or modify it underthe terms of the GNU General Public License as published by the Free SoftwareFoundation; either version 2 of the License, or (at your option) any laterversion.This program is distributed in the hope that it will be useful, but WITHOUT ANYWARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR APARTICULAR PURPOSE. See the GNU General Public License for more details.You should have received a copy of the GNU General Public License along withthis program; if not, write to the Free Software Foundation, Inc., 59 TemplePlace, Suite 330, Boston, MA 02111-1307 USAAuthor contact information:engelen@genivia.com / engelen@acm.org--------------------------------------------------------------------------------A commercial use license is available from Genivia, Inc., contact@genivia.com-------------------------------------------------------------------------------- The Web server handles HTTP GET requests to serve pages and HTTP POST reguests to handle SOAP/XML messages. This example only implements a simple calculator XML Web service for demonstration purposes (the service responds with SOAP/XML). This application requires Zlib and Pthreads (you can replace Pthreads with another thread library, but you need to study the OpenSSL thread changes in the OpenSSL documentation). On Unix/Linux, please enable SIGPIPE handling, see main function below. SIGPIPE handling will avoid your server from termination when sockets are disconnected by clients before the transaction was completed (aka broken pipe). Compile without OpenSSL: soapcpp2 -c -n -popt opt.h soapcpp2 -c webserver.h Customize your COOKIE_DOMAIN in this file gcc -DWITH_COOKIES -DWITH_ZLIB -o webserver webserver.c options.c plugin/httpget.c plugin/httpform.c plugin/logging.c stdsoap2.c soapC.c soapClient.c soapServer.c -lpthread -lz Compile with OpenSSL: soapcpp2 -c -n -popt opt.h soapcpp2 -c webserver.h Customize your COOKIE_DOMAIN in this file gcc -DWITH_OPENSSL -DWITH_COOKIES -DWITH_ZLIB -o webserver webserver.c options.c plugin/httpget.c plugin/httpform.c plugin/logging.c stdsoap2.c soapC.c soapClient.c soapServer.c -lpthread -lz -lssl -lcrypto Use (HTTP GET): Compile the web server as explained above Start the web server on an even numbered port (e.g. 8080): > webserver 8080 & Start a web browser and open a (localhost) location: http://127.0.0.1:8080 and type userid 'admin' and passwd 'guest' to gain access Open the location: http://127.0.0.1:8080/calc.html then enter an expression Open the locations: http://127.0.0.1:8080/test.html http://127.0.0.1:8081/webserver.wsdl Use (HTTPS GET): Create the SSL certificate Compile the web server with OpenSSL as explained above Start the web server on an odd numbered port (e.g. 8081) > webserver 8081 & Actually, you can start two servers, one on 8080 and a secure one on 8081 Start a web browser and open a (localhost) location: https://127.0.0.1:8081 and type userid 'admin' and passwd 'guest' to gain access Open the location: https://127.0.0.1:8081/calc.html and enter an expression Open the locations: https://127.0.0.1:8081/test.html https://127.0.0.1:8081/webserver.wsdl Use (HTTP POST): Serves SOAP/XML calculation requests Command-line options: -z enables compression -c enables chunking -k enables keep-alive -i enables non-threaded iterative server -v enables verbose mode -o<num> pool of <num> threads (cannot be used with option -i) Note: interactive chunking/keep-alive settings cannot be changed, unless the number of threads is interactively changed to restart the pool Note: <num>=0 specifies unlimited threads -t<num> sets I/O timeout value (seconds) -s<num> sets server timeout value (seconds) -d<host> sets cookie domain -p<path> sets cookie path -l[none inbound outbound both] enables logging Requires options.h and options.c for command line option parsing and for parsing interactive Web page options settings. The default_options[] array defines application options, short-hands, selection lists, and default values. See options.h for more details.*/#include "soapH.h"#include "webserver.nsmap"#include "options.h"#include "httpget.h"#include "httpform.h"#include "logging.h"#include "threads.h"/* #include "httpda.h" */ /* enable HTTP Digest Authentication */#include <signal.h> /* defines SIGPIPE */#define BACKLOG (100)#define AUTH_REALM "gSOAP Web Server Admin"#define AUTH_USERID "admin" /* user ID to access admin pages */#define AUTH_PASSWD "guest" /* user pw to access admin pages *//******************************************************************************\ * * Thread pool and request queue *\******************************************************************************/#define MAX_THR (100)#define MAX_QUEUE (1000)static int poolsize = 0;static int queue[MAX_QUEUE];static int head = 0, tail = 0;static MUTEX_TYPE queue_cs;static COND_TYPE queue_cv;/******************************************************************************\ * * Program options *\******************************************************************************/static const struct option default_options[] ={ { "z.compress", NULL, }, { "c.chunking", NULL, }, { "k.keepalive", NULL, }, { "i.iterative", NULL, }, { "v.verbose", NULL, }, { "o.pool", "threads", 6, "none"}, { "t.ioTimeout", "seconds", 6, "5"}, { "s.serverTimeout", "seconds", 6, "3600"}, { "d.cookieDomain", "host", 20, "localhost"}, { "p.cookiePath", "path", 20, "/"}, { "l.logging", "none inbound outbound both", }, { "", "port", }, /* rest of command line args */ { NULL }, /* must be NULL terminated */};/* The numbering of these defines must correspond to the option sequence */#define OPTION_z 0#define OPTION_c 1#define OPTION_k 2#define OPTION_i 3#define OPTION_v 4#define OPTION_o 5#define OPTION_t 6#define OPTION_s 7#define OPTION_d 8#define OPTION_p 9#define OPTION_l 10#define OPTION_port 11/******************************************************************************\ * * Static *\******************************************************************************/static struct option *options = NULL;static time_t start;static int secure = 0; /* =0: no SSL, =1: support SSL */static const char *minutes[60] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" };static const char *hours[24] = {"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23"};/******************************************************************************\ * * Forward decls *\******************************************************************************/void server_loop(struct soap*);void *process_request(void*); /* multi-threaded request handler */void *process_queue(void*); /* multi-threaded request handler for pool */int enqueue(SOAP_SOCKET);SOAP_SOCKET dequeue();int http_get_handler(struct soap*); /* HTTP get handler */int http_form_handler(struct soap*); /* HTTP form handler */int check_authentication(struct soap*); /* HTTP authentication check */int copy_file(struct soap*, const char*, const char*); /* copy file as HTTP response */int calcget(struct soap*);int calcpost(struct soap*);int info(struct soap*);int html_hbar(struct soap*, const char*, size_t, size_t, unsigned long);int html_hist(struct soap*, const char*, size_t, size_t, size_t, const char**, size_t*, size_t);void sigpipe_handle(int); /* SIGPIPE handler: Unix/Linux only *//******************************************************************************\ * * OpenSSL *\******************************************************************************/int CRYPTO_thread_setup();void CRYPTO_thread_cleanup();/******************************************************************************\ * * Main *\******************************************************************************/int main(int argc, char **argv){ struct soap soap; SOAP_SOCKET master; int port = 0; start = time(NULL); options = copy_options(default_options); /* must copy, so option values can be modified */ if (parse_options(argc, argv, options)) exit(0); if (options[OPTION_port].value) port = atol(options[OPTION_port].value); if (!port) port = 8080; fprintf(stderr, "Starting Web server on port %d\n", port); /* if the port is an odd number, the Web server uses HTTPS only */ if (port % 2) secure = 1; if (secure) fprintf(stderr, "[Note: use https://localhost:%d/test.html to test the server from browser]\n", port); else fprintf(stderr, "[Note: use http://localhost:%d/test.html to test the server from browser]\n", port); fprintf(stderr, "[Note: you should enable Linux/Unix SIGPIPE handlers to avoid broken pipe]\n"); soap_init2(&soap, SOAP_IO_KEEPALIVE, SOAP_IO_DEFAULT);#ifdef WITH_OPENSSL if (CRYPTO_thread_setup()) { fprintf(stderr, "Cannot setup thread mutex\n"); exit(1); } /* SSL (to enable: compile all sources with -DWITH_OPENSSL) */ if (secure && soap_ssl_server_context(&soap, SOAP_SSL_DEFAULT, "server.pem", /* keyfile: see SSL docs on how to obtain this file */ "password", /* password to read the key file */ NULL, /* cacert */ NULL, /* capath */ "dh512.pem", /* DH file, if NULL use RSA */ NULL, /* if randfile!=NULL: use a file with random data to seed randomness */ "webserver" /* server identification for SSL session cache (must be a unique name) */ )) { soap_print_fault(&soap, stderr); exit(1); }#endif /* Register HTTP GET plugin */ if (soap_register_plugin_arg(&soap, http_get, (void*)http_get_handler)) soap_print_fault(&soap, stderr); /* Register HTTP POST plugin */ if (soap_register_plugin_arg(&soap, http_form, (void*)http_form_handler)) soap_print_fault(&soap, stderr); /* Register logging plugin */ if (soap_register_plugin(&soap, logging)) soap_print_fault(&soap, stderr);#ifdef HTTPDA_H /* Register HTTP Digest Authentication plugin */ if (soap_register_plugin(&soap, http_da)) soap_print_fault(&soap, stderr);#endif /* Unix SIGPIPE, this is OS dependent (win does not need this) */ /* soap.accept_flags = SO_NOSIGPIPE; */ /* some systems like this */ /* soap.socket_flags = MSG_NOSIGNAL; */ /* others need this */ /* signal(SIGPIPE, sigpipe_handle); */ /* and some older Unix systems may require a sigpipe handler */ master = soap_bind(&soap, NULL, port, BACKLOG); if (!soap_valid_socket(master)) { soap_print_fault(&soap, stderr); exit(1); } fprintf(stderr, "Port bind successful: master socket = %d\n", master); MUTEX_SETUP(queue_cs); COND_SETUP(queue_cv); server_loop(&soap); MUTEX_CLEANUP(queue_cs); COND_CLEANUP(queue_cv); free_options(options); soap_end(&soap); soap_done(&soap);#ifdef WITH_OPENSSL CRYPTO_thread_cleanup();#endif THREAD_EXIT; return 0;}void server_loop(struct soap *soap){ struct soap *soap_thr[MAX_THR]; THREAD_TYPE tid, tids[MAX_THR]; int req; struct logging_data *logdata; logdata = (struct logging_data*)soap_lookup_plugin(soap, logging_id); /* need to access plugin's data */ for (req = 1; ; req++) { SOAP_SOCKET sock; int newpoolsize; soap->cookie_domain = options[OPTION_d].value; soap->cookie_path = options[OPTION_p].value; soap_set_cookie(soap, "visit", "true", NULL, NULL); soap_set_cookie_expire(soap, "visit", 600, NULL, NULL); if (options[OPTION_c].selected) soap_set_omode(soap, SOAP_IO_CHUNK); /* use chunked HTTP content (fast) */ if (options[OPTION_k].selected) soap_set_omode(soap, SOAP_IO_KEEPALIVE); if (options[OPTION_t].value) soap->send_timeout = soap->recv_timeout = atol(options[OPTION_t].value); if (options[OPTION_s].value) soap->accept_timeout = atol(options[OPTION_s].value); if (options[OPTION_l].selected == 1 || options[OPTION_l].selected == 3) logdata->inbound = stdout; else logdata->inbound = NULL; if (options[OPTION_l].selected == 2 || options[OPTION_l].selected == 3) logdata->outbound = stdout; else logdata->outbound = NULL; newpoolsize = atol(options[OPTION_o].value); if (newpoolsize < 0) newpoolsize = 0; else if (newpoolsize > MAX_THR) newpoolsize = MAX_THR; if (poolsize > newpoolsize) { int job; for (job = 0; job < poolsize; job++) { while (enqueue(SOAP_INVALID_SOCKET) == SOAP_EOM) sleep(1); } for (job = 0; job < poolsize; job++) { fprintf(stderr, "Waiting for thread %d to terminate...\n", job); THREAD_JOIN(tids[job]); fprintf(stderr, "Thread %d has stopped\n", job); soap_done(soap_thr[job]); free(soap_thr[job]); } poolsize = 0; } if (poolsize < newpoolsize) { int job; for (job = poolsize; job < newpoolsize; job++) { soap_thr[job] = soap_copy(soap); if (!soap_thr[job]) break; soap_thr[job]->user = (void*)job; fprintf(stderr, "Starting thread %d\n", job); THREAD_CREATE(&tids[job], (void*(*)(void*))process_queue, (void*)soap_thr[job]); } poolsize = job; } sock = soap_accept(soap); if (!soap_valid_socket(sock)) { if (soap->errnum) { soap_print_fault(soap, stderr); fprintf(stderr, "Retry...\n"); continue; } fprintf(stderr, "gSOAP Web server timed out\n"); break; } if (options[OPTION_v].selected) fprintf(stderr, "Request #%d accepted on socket %d connected from IP %d.%d.%d.%d\n", req, sock, (int)(soap->ip>>24)&0xFF, (int)(soap->ip>>16)&0xFF, (int)(soap->ip>>8)&0xFF, (int)soap->ip&0xFF); if (poolsize > 0) { while (enqueue(sock) == SOAP_EOM) sleep(1); } else { struct soap *tsoap = NULL; if (!options[OPTION_i].selected) tsoap = soap_copy(soap); if (tsoap) {#ifdef WITH_OPENSSL if (secure && soap_ssl_accept(tsoap))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -