diff --git a/Makefile b/Makefile index 11f90ac2..3d85d5d7 100644 --- a/Makefile +++ b/Makefile @@ -90,7 +90,6 @@ install-notice: echo " chown -R nsadmin:nsadmin $(NAVISERVER)/logs"; \ echo ""; \ user="-u nsadmin"; \ - chown -R nobody $(NAVISERVER)/logs; \ fi; \ echo "You can now run NaviServer by typing the following command: "; \ echo ""; \ diff --git a/configure.ac b/configure.ac index 22ac985e..8763ad2e 100644 --- a/configure.ac +++ b/configure.ac @@ -35,7 +35,7 @@ AC_PREREQ([2.68]) -AC_INIT([NaviServer],[4.99.16d7],[naviserver-devel@lists.sourceforge.net]) +AC_INIT([NaviServer],[4.99.16d10],[naviserver-devel@lists.sourceforge.net]) AC_CONFIG_MACRO_DIR([m4]) #AM_INIT_AUTOMAKE([1.9 -Wall -Werror foreign]) diff --git a/doc/src/manual/admin-maintenance.man b/doc/src/manual/admin-maintenance.man index 94bf0fb6..49119589 100644 --- a/doc/src/manual/admin-maintenance.man +++ b/doc/src/manual/admin-maintenance.man @@ -26,7 +26,7 @@ Options: [item] [emph {-c}] [para] - Run in command (interactive) mode, when started Tcl shell will be provided + Run in command (interactive) mode, when started Tcl shell will be provided same way as tclsh [item] [emph {-f}] @@ -114,7 +114,7 @@ To restart the server from the shell you can send the server the SIGINT. [list_begin itemized] [item] Builtin feature. No need to setup something else. -[item] Ability to restart from within your application. +[item] Ability to restart from within your application. [list_end] @@ -123,7 +123,7 @@ To restart the server from the shell you can send the server the SIGINT. [list_begin itemized] [item] First step of implementation. You may need more options for the watchdog as - it offers you now. + it offers you now. [list_end] @@ -142,7 +142,7 @@ might be saved under [term /etc/systemd/system/nsd.service] or After=network.target # After=network.target postgresql.service # Wants=postgresql.service - + [lb]Service[rb] Type=forking PIDFile=/usr/local/ns/logs/nsd.pid @@ -150,16 +150,16 @@ might be saved under [term /etc/systemd/system/nsd.service] or # In case, a site is using Google Perfortools malloc with the system-malloc patch for Tcl: # Environment="LD_PRELOAD=/usr/lib64/libtcmalloc.so" ExecStartPre=/bin/rm -f /usr/local/ns/logs/nsd.pid - + # Standard startup with a non-privileged port (like 8e000) ExecStart=/usr/local/ns/bin/nsd -u nsadmin -g nsadmin -t /usr/local/ns/conf/nsd-config.tcl # Startup for privileged port, like 80; the IPv6 address is bound to 2 sockets. # ExecStart=/usr/local/ns/bin/nsd -u nsadmin -g nsadmin -t /usr/local/ns/conf/nsd-config.tcl -b YOUR-IPv4-ADDRESS:80,YOUR-IPv6-ADDRESS:80#2 - + Restart=on-abnormal KillMode=process - + [lb]Install[rb] #WantedBy=multi-user.target [example_end] @@ -188,7 +188,7 @@ and init does the job of restarting the server if it crashes. [list_begin itemized] [item] Very easy to configure. -[item] Some kind of built in feature of your OS. +[item] Some kind of built in feature of your OS. [list_end] @@ -229,7 +229,7 @@ as part of a script every 5 minutes by adding a crontab line (crontab -e) like flexible in choosing the right action for the job, e.g. E-Mail notifications. -[item] Works fine if you don't have a requirement of virtually no downtime. +[item] Works fine if you don't have a requirement of virtually no downtime. [list_end] @@ -257,7 +257,7 @@ Daemontools is a collection of software to control other processes. [list_begin itemized] -[item] Allows more users to control the process, e.g. users of a specific group. +[item] Allows more users to control the process, e.g. users of a specific group. [list_end] @@ -266,7 +266,7 @@ Daemontools is a collection of software to control other processes. [list_begin itemized] [item] Extra package, you need to compile it. -[item] You need to be root. +[item] You need to be root. [list_end] @@ -286,21 +286,19 @@ So it's possible to tell NaviServer to run as root using the -u switch at startu Of course, you have to be root to do that: [example_begin] - -./nsd -f -u root -g www -t ../sample-config.tcl - + ./nsd -f -u root -g www -t ../sample-config.tcl [example_end] You can see the result in the security info: [example_begin] -... -[lb]-main-[rb] Notice: nsmain: NaviServer/4.99.15 starting -[lb]-main-[rb] Notice: nsmain: security info: uid=0, euid=0, gid=8, egid=8 -... + ... + [lb]-main-[rb] Notice: nsmain: NaviServer/4.99.15 starting + [lb]-main-[rb] Notice: nsmain: security info: uid=0, euid=0, gid=8, egid=8 + ... [example_end] -It's simply a convenience for people that need it to not have to rewrite code to achieve this. +It's simply a convenience for people that need it to not have to rewrite code to achieve this. [subsection {Running inside Chroot Environment}] @@ -325,7 +323,7 @@ to simplify and speed up the task of creating a chroot environment. [item] Usually strong barrier for attackers [item] Access to the filesystem is limited -[item] Hopefully limits the damage of previously unknown, new security holes +[item] Hopefully limits the damage of previously unknown, new security holes [list_end] @@ -337,7 +335,7 @@ to simplify and speed up the task of creating a chroot environment. [item] The setup of a chroot cage is a cumbersome task, you have to find out every library used, absolute paths might be compiled into the code, strace will become your friend... [item] Every other application used (e.g. ImageMagick) must be chrooted [item] When using a database network support must be enabled or the socket placed into the chroot environment (?) -[item] Updates and patches for NaviServer or any other chrooted tool becomes more difficult +[item] Updates and patches for NaviServer or any other chrooted tool becomes more difficult [list_end] @@ -345,7 +343,7 @@ to simplify and speed up the task of creating a chroot environment. [emph {Alternatives}] -Use Novell AppArmor (http://www.novell.com/products/apparmor/) and create a Profile for NaviServer +Use Novell AppArmor (http://www.novell.com/products/apparmor/) and create a Profile for NaviServer [subsection {Command (interactive) mode}] @@ -356,9 +354,7 @@ added for developers: The interactive 'Command mode'. If you start NaviServer with [example_begin] - ./nsd -c - [example_end] it runs a Tcl shell and waits for commands. You are then able to do more comprehensive @@ -380,59 +376,55 @@ issued, [cmd "ns_info threads"] and [cmd "ns_server requestprocs"]. [example_begin] - -buckel@tulpe:/usr/local/ns/bin> ./nsd -c -u nsadmin -g www -t ../sample-config.tcl -[lb]-main-[rb] Notice: encoding: loaded: iso8859-1 -[lb]-main-[rb] Notice: nsmain: NaviServer/4.99.15 starting -[lb]-main-[rb] Notice: nsmain: security info: uid=500, euid=500, gid=100, egid=100 -[lb]-main-[rb] Notice: nsmain: max files: FD_SETSIZE = 1024, rl_cur = 1024, rl_max = 1024 -[lb]-main-[rb] Notice: adp[lb]server1[rb]: mapped /*.adp -[lb]-main-[rb] Notice: modload: loading '/usr/local/ns/bin/nssock.so' -[lb]-main-[rb] Notice: modload: loading '/usr/local/ns/bin/nslog.so' -[lb]-main-[rb] Notice: nslog: opened '/usr/local/ns/servers/server1/modules/nslog/access.log' -[lb]-main-[rb] Notice: conf: [lb]ns/server/server1[rb]enabletclpages = 0 -[lb]-main-[rb] Notice: nsmain: NaviServer/4.95.0 running -[lb]-main-[rb] Notice: nsmain: security info: uid=500, euid=500, gid=100, egid=100 -[lb]-sched-[rb] Notice: sched: starting -[lb]-main-[rb] Notice: nssock: listening on 10.0.0.100:8000 -[lb]-driver-[rb] Notice: starting -[lb]-driver-[rb] Notice: driver: accepting connections -% ns_info threads -{-driver- -main- 1084140464 0 1119862183 p:0x40037cfb a:0x0} -{-sched- -main- 1082039216 0 1119862183 p:0x4004a09a a:0x0} -{-main- {} 1076887680 1 1119862183 p:0x0 a:0x0} -% ns_server requestprocs -{GET / * inherit ns:fastget a:0x0} {HEAD / * inherit ns:fastget a:0x0} {POST / * inherit ns:fastget a:0x0} -% - + buckel@tulpe:/usr/local/ns/bin> ./nsd -c -u nsadmin -g www -t ../sample-config.tcl + [lb]-main-[rb] Notice: encoding: loaded: iso8859-1 + [lb]-main-[rb] Notice: nsmain: NaviServer/4.99.15 starting + [lb]-main-[rb] Notice: nsmain: security info: uid=500, euid=500, gid=100, egid=100 + [lb]-main-[rb] Notice: nsmain: max files: FD_SETSIZE = 1024, rl_cur = 1024, rl_max = 1024 + [lb]-main-[rb] Notice: adp[lb]server1[rb]: mapped /*.adp + [lb]-main-[rb] Notice: modload: loading '/usr/local/ns/bin/nssock.so' + [lb]-main-[rb] Notice: modload: loading '/usr/local/ns/bin/nslog.so' + [lb]-main-[rb] Notice: nslog: opened '/usr/local/ns/servers/server1/modules/nslog/access.log' + [lb]-main-[rb] Notice: conf: [lb]ns/server/server1[rb]enabletclpages = 0 + [lb]-main-[rb] Notice: nsmain: NaviServer/4.95.0 running + [lb]-main-[rb] Notice: nsmain: security info: uid=500, euid=500, gid=100, egid=100 + [lb]-sched-[rb] Notice: sched: starting + [lb]-main-[rb] Notice: nssock: listening on 10.0.0.100:8000 + [lb]-driver-[rb] Notice: starting + [lb]-driver-[rb] Notice: driver: accepting connections + % ns_info threads + {-driver- -main- 1084140464 0 1119862183 p:0x40037cfb a:0x0} + {-sched- -main- 1082039216 0 1119862183 p:0x4004a09a a:0x0} + {-main- {} 1076887680 1 1119862183 p:0x0 a:0x0} + % ns_server requestprocs + {GET / * inherit ns:fastget a:0x0} {HEAD / * inherit ns:fastget a:0x0} {POST / * inherit ns:fastget a:0x0} + % [example_end] [subsection {Examples}] -[para] - -[emph {nsd -t nsd.tcl}] +[example_begin] + nsd -t nsd.tcl +[example_end] [para] - This is the simplest form of the command line. [para] -[emph {nsd -ft nsd.tcl}] +[example_begin] + nsd -ft nsd.tcl +[example_end] [para] - NaviServer will be run in the foreground. -[para] - -[emph {nsd -fkt nsd.tcl -r /newroot}] - -[para] +[example_begin] + nsd -fkt nsd.tcl -r /newroot +[example_end] -NaviServer will be run in a chroot environment, and any +[para] NaviServer will be run in a chroot environment, and any currently-running server will be killed. @@ -581,320 +573,166 @@ the associated NaviServer features. [para] -[emph {/conf}] +[subsection {/conf}] -[para] -Directory containing the NaviServer configuration files. This is convention and convenience, the config files can be located anywhere as nsd process requires path to config file. +[para] Directory containing the NaviServer configuration files. This +is convention and convenience, the config files can be located +anywhere as nsd process requires path to config file. [para] Files: - -[para] .tcl files nsd.tcl sample-config.tcl simple-config.tcl -[para] - -[emph {/bin}] +[subsection {/bin}] [para] - - Directory containing the NaviServer binary and the default directory for any dynamically-loadable C modules. - [para] - - Files: + .so files, nsd, tclsh, ... -[para] - - - .so files - nsd - translate-ini - - -[para] - - -[emph {/include}] - +[subsection {/include}] [para] - - -Directory containing header files for NaviServer. - +Directory containing header files for NaviServer. These files are +primarlily needed for compiling additional C modules. [para] +Files: .h files -Files: +[subsection {/lib}] [para] - - - .h files - - -[para] - - -[emph {/lib}] - - -[para] - - Directory containing static libraries used for building customized components to NaviServer. This directory currently only includes the libnspd.a file that can be used to build database proxy daemons (external database drivers). - [para] - - Files: + libnsd.so, + libnsdb.so, + libnsthread.so, + libnsproxy.so, + ... -[para] - - - libnsd.so - libnsdb.so - libnsthread.so - libnsproxy.so - +[subsection {/logs}] [para] - - -[emph {/logs}] - - -[para] - - Directory containing log files and the server pid file. - [para] - - Files: - -[para] - - - access.log - server.log + access.log, + server.log, nsd.pid -[para] - - -[emph {/modules}] - +[subsection {/modules}] [para] - - Contains directories for each configured module that operates across -servers, such as the Tcl module. - - -[para] - - -[emph {/modules/nscp}] - - -[para] - - -Directory containing files related to control port interface. - - -[para] - - -Files: - - -[para] +servers, such as the "tcl" or "nsperm" module. - tcsh.inputrc +[subsection {/modules/nsperm}] +[para] Directory containing user, group, and permissions files which +is used to provide access control for NaviServer. [para] - - - -[emph {/tcl}] - - -[para] - - -Default directory for shared Tcl script library. Also contains -subdirectories containing Tcl examples and Tcl scripts for various -modules. - - -[para] - - Files: + passwd, group, hosts.allow, hosts.deny, perms -[para] - - - .tcl files - +[subsection {/modules/tcl}] [para] - - -[emph {/modules/nsperm}] - - -[para] - - -Directory containing user, group, and permissions files which is used to provide access control -for NaviServer. - - -[para] - - -Files: - - -[para] - - - passwd file - group file - hosts.allow file - hosts.deny file - perms file - - -[para] - - -[emph {/modules/tcl}] - - -[para] - - Default directory for private Tcl script library for this server. - [para] +Files: .tcl files -Files: +[subsection {/pages}] +[para] Default directory where pages and images for the server are +stored. Users define typically variaious subdirectories of this +directory. [para] +Files: typically, .htm, .html, .shtml, and .adp files - .tcl files - - -[para] - - - -[emph {/pages}] - - -[para] - - -Default directory where pages and graphics for the server are stored. -Users can optionally have individual subdirectories of this directory -(see UserDir, below). +[subsection {/tcl}] +[para] Default directory for shared Tcl script library. Also contains +subdirectories containing Tcl examples and Tcl scripts for various +modules. The toplevel files of this directory are loaded into +NaviServer during startup. [para] - - Files: + .tcl files -[para] - - - typically, .htm, .html, .shtml, and .adp files - - -[para] - [section {Security Guide}] - This chapter provides guidelines for ensuring the security of systems running NaviServer. It describes the issues that must be considered and the associated modifications that should be made to NaviServer installations. -[para] [subsection {General nsadmin Passwords}] - -[para] - - -By default, the nsadmin password for NaviServer is either set to NULL -or to a poor password. Set an acceptable password for nsadmin as -described below. - - -[para] - - -Edit the nsadmin entry in the /modules/nsperm/passwd file. For -example, the default passwd file contains this nsadmin entry: - nsadmin:CUdnvgBYocLSI::::: - - -[para] +[para] By default, the nsadmin password for NaviServer is either set +to NULL or to a poor password. Set an acceptable password for nsadmin +as described below. -Substitute an alternate encrypted password in place of CUdnvgBYocLSI. +[para] Edit the nsadmin entry in the [file /modules/nsperm/passwd] +file. For example, the default passwd file contains this nsadmin +entry: +[example_begin] + nsadmin:CUdnvgBYocLSI::::: +[example_end] [para] +Substitute an alternate encrypted password in place of +[term CUdnvgBYocLSI]. +[para] To produce a hash for a new password, use [cmd ns_crypt] in +Command mode (using "nsd -c"): -To encrypt a password, you can copy an already-encrypted password from -the /etc/passwd file or run the bin/nspasswd utility. It will prompt -you for a password and return the encrypted version of the password. +[example_begin] + % ns_crypt MyNewPassword xx + xxhR1Y2vt4OOY +[example_end] +Alternatively, the same hash can be produced from the command line +using e.g. Perl: -[para] +[example_begin] + $ perl -le 'print crypt("MyNewPassword","xx");' + xxhR1Y2vt4OOY +[example_end] -For more information about the passwd file, see the "Defining Users" -section. +[para] For more information about the passwd file, see the "Defining +Users" section. [para] @@ -1117,20 +955,13 @@ are: [list_begin itemized] [item] File system related functions, such as open, read, and puts - [item] The NaviServer ns_sock* Tcl functions - [item] The Tcl socket routines - [item] The exec command - [item] The file command, or at least the delete and rename features - [item] The exit command - [list_end] - [para] @@ -1147,7 +978,7 @@ This code example disables the open command: Tcl_CreateCommand(interp, "open", BadCmd, NULL, NULL); return TCL_OK; } - + static int BadCmd(ClientData dummy, Tcl_Interp *interp, int argc, char **argv) { Tcl_AppendResult(interp, "disabled command: ", argv[lb]0[rb], NULL); @@ -1165,27 +996,24 @@ the stacksize parameter of your config file. As a rule of thumb the default stacksize (in bytes) [example_begin] -# -# Thread library (nsthread) parameters -# -ns_section "ns/threads" -ns_param stacksize [expr 64*1024] ;# Per-thread stack size. - + # + # Thread library (nsthread) parameters + # + ns_section "ns/threads" + ns_param stacksize [expr 64*1024] ;# Per-thread stack size. [example_end] is almost always not enough. Use multiples of 1024 and increase to 1 Megabyte for the start: [example_begin] - -# -# Thread library (nsthread) parameters -# -ns_section "ns/threads" -ns_param stacksize [expr 1024*1024] ;# Per-thread stack size. - + # + # Thread library (nsthread) parameters + # + ns_section "ns/threads" + ns_param stacksize [expr 1024*1024] ;# Per-thread stack size. [example_end] -If the size of the stack is not enough you may encounter crashes without real explanation. +If the size of the stack is not enough you may encounter crashes without real explanation. [subsection {Database Access}] diff --git a/doc/src/manual/tcl-overview.man b/doc/src/manual/tcl-overview.man index 3b72ef60..d7f1411e 100644 --- a/doc/src/manual/tcl-overview.man +++ b/doc/src/manual/tcl-overview.man @@ -213,49 +213,48 @@ better-suited to different situations: [section {Tcl Interpreters}] - During NaviServer initialization, only one interpreter exists. While - modules are loaded and initialized, they may add procedures to the - interpreter. When initialization is complete (all modules are loaded - and all Tcl libraries have been executed), the interpreter may no - longer be changed. - + During NaviServer initialization, only one interpreter + exists. While modules are loaded and initialized, they may add + procedures to the interpreter. When initialization is complete (all + modules are loaded and all Tcl libraries have been executed), + NaviServer creates from this a blueprint consisting of the + sources. This blueprint is provided to every thread at + initialization. [para] - - Each connection thread that requires Tcl will create a copy of the - original interpreter. - + When e.g. a connection thread processes a request, all the Tcl code + of the blueprint is immediately available (without any latency for + loading). [para] - + NaviServer provides built-in support for updating a blueprint, + e.g. via [cmd ns_eval] or via the lower level [cmd ns_ictl] + commands. [section {Global Variables}] - - Tcl interpreter has its own global variable table, so global - variables are shared only within each Tcl interpreter by default. The - nsv_ commands allows you to make a variable available to all - interpreters in a virtual server. - + The NaviServer blueprint contains Tcl functions, XOTcl/NX classes + and objects and also namespaced variables. This are all Tcl + variables, which are not in the topmost namespace "::". The + toplevel variables are treated as "scratch variables", which are + flushed automatically after every request. The global variables are + often used for providing values for templating; cleaning these + automatically can avoid unexpected interactions. [para] - - Global variables are flushed when the Tcl interpreter is deallocated. - A Tcl interpreter is deallocated explicitly when - ns_TclDeallocateInterp is called or implicitly at the end of a - connection. This allows Tcl filter functions to access global - variables set in ADPs. - - + In addition of the per-interpreter variables, NaviServer provides + shared variables, which can be accessed via the nsv_* interface + (see below for an example). When such a variable is altered, its + value is immediately available in all threads. [section {Sharing Files Between Interpreters}] - To share vairables between the interpreters in a group, use the Tcl + To share variables between the interpreters in a group, use the Tcl nsv_* command. [para] diff --git a/doc/src/naviserver/ns_base64decode.man b/doc/src/naviserver/ns_base64decode.man index ef618537..2be7ddcb 100644 --- a/doc/src/naviserver/ns_base64decode.man +++ b/doc/src/naviserver/ns_base64decode.man @@ -2,7 +2,7 @@ [manpage_begin ns_base64decode n [vset version]] [moddesc {NaviServer Built-in Commands}] -[titledesc {Binary-to-text decoding using "base64" encoding}] +[titledesc {Text-to-binary decoding using "base64" encoding}] [description] diff --git a/doc/src/naviserver/ns_cache.man b/doc/src/naviserver/ns_cache.man index 725b82fe..b9747bc5 100644 --- a/doc/src/naviserver/ns_cache.man +++ b/doc/src/naviserver/ns_cache.man @@ -192,12 +192,10 @@ If the cache value does not already exist it is created. [opt [arg args]] ] Flush the entries in a cache and return the number of flushed entries. -If [arg args] are given then they are the keys in the cache to be -flushed. If the [option -glob] option is given then the keys are -treated as globbing patterns and only the keys which match are -flushed. - - +If the optional [arg args] are given these are used as the keys in the +cache to be flushed. If the [option -glob] option is given then the +keys are treated as globbing patterns and only the entries with +matching keys are flushed. [call [cmd ns_cache_stats] \ [opt [option "-contents"]] \ @@ -255,6 +253,34 @@ way for a new entry. [list_end] +[call [cmd ns_cache_transaction_begin]] + +Begin a cache transaction. A cache transaction provides in essence the +ability to rollback the added/updated values, while providing cache +isolations to other transactions. The cached values from incomplete +cache transactions are just visible from the current thread, but not +from other threads. Cache transactions effect always all caches. + +[para] Typically, cache transactions are used in accordance with +database transactions. + +[call [cmd ns_cache_transaction_commit] \ + [opt [option "-all"]]] + +Successfully terminate a cache transaction. All added values are made +visible to other threads. The option [option "-all"] can be used to +commit all nested transactions currently open in this thread. + +[call [cmd ns_cache_transaction_rollback] \ + [opt [option "-all"]]] + +Terminate a cache transaction and undo changes in all caches since +the matching [cmd ns_cache_transaction_begin]. +The option [option "-all"] can be used to +rollback all nested transactions currently open in this thread. + + + [call [cmd ns_fastpath_cache_stats] \ [opt [option "-contents"]] \ [opt [option "-reset"]] \ diff --git a/doc/src/naviserver/ns_cookie.man b/doc/src/naviserver/ns_cookie.man index 79e508c8..2584a8a8 100644 --- a/doc/src/naviserver/ns_cookie.man +++ b/doc/src/naviserver/ns_cookie.man @@ -9,7 +9,7 @@ Cookies are small pieces of data which the server sends to the [term "user agent"] and the user agent sends back during subsequent requests. You can use this -mechanism to save state between otherwise statelss HTTP requests. +mechanism to save state between otherwise stateless HTTP requests. [para] Cookie size limits are user agent dependent. Practically, as cookies are sent @@ -54,6 +54,7 @@ The [arg data] will be URL-encoded to escape special characters. [call [cmd ns_getcookie] \ + [opt [option "-all [arg bool]"]] \ [opt [option "-include_set_cookies [arg bool]"]] \ [opt [arg --]] \ [arg name] \ @@ -66,13 +67,17 @@ true, then this command will search as well in the cookies being currently set in the output headers. [para] -Only the first cookie identified by [arg name] is returned. If two cookies +By default, only the first cookie identified by [arg name] is returned. If two cookies with the same name are sent by the user agent, each with a different [arg domain] -and/or [arg path], which cookie data is returned depends on the user agent. +and/or [arg path], which cookie data is returned depends on the user +agent. When the option [opt -all] is set, all cookies with the +specified name are returned. This can be use to detect such situations +with multiple cookies. The option [opt -all] cannot be set together +with the option [opt -include_set_cookies]. [para] -When using [arg domain] or [arg path] to restrict cookies, use a unique name for -each restricted cookie. +When using [arg domain] or [arg path] to restrict cookies, it is +recommended to use a unique name for each restricted cookie. [call [cmd ns_deletecookie] \ @@ -96,7 +101,7 @@ for the cookie to be deleted. If the provided domain is empty, it is ignored. [list_begin options] [opt_def -discard [arg boolean]] -If the discard option is true then the cookie will be automaticalled +If the discard option is true then the cookie will be automatically deleted at the end of the current session (when the user agent terminates). [opt_def -secure [arg boolean]] diff --git a/doc/src/naviserver/ns_kill.man b/doc/src/naviserver/ns_kill.man index 098505ad..f694c508 100644 --- a/doc/src/naviserver/ns_kill.man +++ b/doc/src/naviserver/ns_kill.man @@ -22,8 +22,8 @@ no error will be returned on failure. signal should be an integer, i.e., 1 for -[see_also nsd] -[keywords ns_job] +[see_also ns_shutdown ns_job] +[keywords restart] [manpage_end] diff --git a/doc/src/naviserver/ns_server.man b/doc/src/naviserver/ns_server.man index 2f2ea975..ddf5ccb3 100644 --- a/doc/src/naviserver/ns_server.man +++ b/doc/src/naviserver/ns_server.man @@ -71,15 +71,21 @@ Returns a list of the mappings from urls to files. [call [cmd ns_server] \ [opt [option "-server [arg s]"]] \ [opt [option "-pool [arg p]"]] \ - [cmd active]] + [cmd active] \ + [opt [option "-checkforproxy"]] \ + ] [call [cmd ns_server] \ [opt [option "-server [arg s]"]] \ [opt [option "-pool [arg p]"]] \ - [cmd all]] + [cmd all] \ + [opt [option "-checkforproxy"]] \ + ] [call [cmd ns_server] \ [opt [option "-server [arg s]"]] \ [opt [option "-pool [arg p]"]] \ - [cmd queued]] + [cmd queued] \ + [opt [option "-checkforproxy"]] \ + ] These three commands return information about queued or running requests. For every request the command returns a list containing @@ -88,6 +94,11 @@ request (HTTP method and url), running time, and bytes sent. The sub-command [arg all] returns the union of the running and queued requests. +[para] When option [option -checkforproxy] is given, it tries to +return the peer address from "X-Forwarded-For" header field. If this +fails (not given, or empty, or having the value "unknown") it falls +back to the physical peer address. + [call [cmd ns_server] \ [opt [option "-server [arg s]"]] \ [opt [option "-pool [arg p]"]] \ diff --git a/doc/src/naviserver/ns_shutdown.man b/doc/src/naviserver/ns_shutdown.man index 37376681..2f8d74af 100644 --- a/doc/src/naviserver/ns_shutdown.man +++ b/doc/src/naviserver/ns_shutdown.man @@ -5,21 +5,30 @@ [titledesc {Shut down NaviServer}] [description] -This command shuts down the server. If [emph timeout] is not specified -a default value (normally 20) is used; if it is specified, it should be -an integer >= 0. The server will wait for up to the specified (or default) -number of seconds for existing connections to finish processing. If this -time limit is exceeded then existing connections are immediately stopped. + +This command shuts down the server, optionally waiting [arg timeout] +seconds to let existing connections and background jobs finish. When +this time limit is exceeded the server shuts down immediately. + +[para] When [arg timeout] is not specified the default or configured +[arg timeout] is used (default 20). The default can be changed by the +parameter [term shutdowntimeout] in the global server parameters +(section [term ns/parameters] in the config file). When [arg timeout] +it is specified, it must be an integer >= 0. [section {COMMANDS}] [list_begin definitions] -[call [cmd ns_shutdown] [arg ?timeout?]] +[call [cmd ns_shutdown] [opt [option -restart]] [opt [arg timeout]]] [para] [arg timeout] -How long to wait before shuting down the server +Time in seconds to wait before shuting down the server + +[para] +[option -restart] +send an interrupt signal to the server, leading to a non-zero exit code. [list_end] @@ -31,7 +40,8 @@ How long to wait before shuting down the server [example_end] -[see_also nsd] +[see_also ns_kill] +[keywords restart] [manpage_end] diff --git a/doc/src/naviserver/nsv.man b/doc/src/naviserver/nsv.man index 88db00d2..5ec695a9 100644 --- a/doc/src/naviserver/nsv.man +++ b/doc/src/naviserver/nsv.man @@ -170,8 +170,8 @@ Set the value for a key in an nsv array. Returns the value the key is set to. [call [cmd nsv_unset] [opt [arg -nocomplain]] [opt --] [arg array] [opt [arg key]]] Unset an array or a single key from an array. If successful returns an -empty string. If -nocomplain is specified as the first argument, the -command does not complain when the specified variable does not exist. +empty string. When [arg -nocomplain] is specified the command does not +complain when the specified array or key does not exist. [example_begin] % nsv_unset shared_array key1 diff --git a/include/ns.h b/include/ns.h index 44f77a3f..f124ae34 100644 --- a/include/ns.h +++ b/include/ns.h @@ -232,7 +232,7 @@ typedef enum { #define Ns_DStringAppendElement Tcl_DStringAppendElement #define Ns_DStringInit Tcl_DStringInit #define Ns_DStringFree Tcl_DStringFree -#define Ns_DStringTrunc Tcl_DStringTrunc +#define Ns_DStringTrunc Tcl_DStringSetLength #define Ns_DStringSetLength Tcl_DStringSetLength #define NS_DSTRING_STATIC_SIZE (TCL_DSTRING_STATIC_SIZE) #define NS_DSTRING_PRINTF_MAX 2048 @@ -256,6 +256,14 @@ typedef struct _Ns_Task Ns_Task; typedef struct _Ns_EventQueue Ns_EventQueue; typedef struct _Ns_Event Ns_Event; +#define NS_CACHE_MAX_TRANSACTION_DEPTH 16 + +typedef struct Ns_CacheTransactionStack { + uintptr_t stack[NS_CACHE_MAX_TRANSACTION_DEPTH]; + int uncommitted[NS_CACHE_MAX_TRANSACTION_DEPTH]; + unsigned int depth; +} Ns_CacheTransactionStack; + /* * This is used for logging messages. @@ -286,7 +294,7 @@ typedef enum { * Global variables: * * LogSeverity, which can be used from modules (e.g. nsssl) - * + * */ NS_EXTERN Ns_LogSeverity Ns_LogTaskDebug; /* Severity at which to log verbose. */ @@ -671,7 +679,7 @@ Ns_AdpFlush(Tcl_Interp *interp, bool doStream) NS_EXTERN Ns_ReturnCode Ns_AuthorizeRequest(const char *server, const char *method, const char *url, - const char *user, const char *passwd, const char *peer) + const char *user, const char *passwd, const char *peer) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(3); NS_EXTERN void @@ -711,6 +719,10 @@ NS_EXTERN Ns_Entry * Ns_CacheFindEntry(Ns_Cache *cache, const char *key) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2); +NS_EXTERN Ns_Entry * +Ns_CacheFindEntryT(Ns_Cache *cache, const char *key, const Ns_CacheTransactionStack *transactionStackPtr) + NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2); + NS_EXTERN Ns_Entry * Ns_CacheCreateEntry(Ns_Cache *cache, const char *key, int *newPtr) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(3); @@ -720,6 +732,11 @@ Ns_CacheWaitCreateEntry(Ns_Cache *cache, const char *key, int *newPtr, const Ns_Time *timeoutPtr) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(3); +NS_EXTERN Ns_Entry * +Ns_CacheWaitCreateEntryT(Ns_Cache *cache, const char *key, int *newPtr, + const Ns_Time *timeoutPtr, const Ns_CacheTransactionStack *transactionStackPtr) + NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(3); + NS_EXTERN const char * Ns_CacheKey(const Ns_Entry *entry) NS_GNUC_NONNULL(1); @@ -733,19 +750,37 @@ Ns_CacheGetSize(const Ns_Entry *entry) NS_GNUC_NONNULL(1); NS_EXTERN const Ns_Time * -Ns_CacheGetExpirey(const Ns_Entry *entry); +Ns_CacheGetExpirey(const Ns_Entry *entry) + NS_GNUC_NONNULL(1); + +NS_EXTERN uintptr_t +Ns_CacheGetTransactionEpoch(const Ns_Entry *entry) + NS_GNUC_NONNULL(1); + +NS_EXTERN unsigned long +Ns_CacheCommitEntries(Ns_Cache *cache, uintptr_t epoch) + NS_GNUC_NONNULL(1); + +NS_EXTERN unsigned long +Ns_CacheRollbackEntries(Ns_Cache *cache, uintptr_t epoch) + NS_GNUC_NONNULL(1); NS_EXTERN void Ns_CacheSetValue(Ns_Entry *entry, void *value) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2); +NS_EXTERN void * +Ns_CacheGetValueT(const Ns_Entry *entry, const Ns_CacheTransactionStack *transactionStackPtr) + NS_GNUC_NONNULL(1); + NS_EXTERN void Ns_CacheSetValueSz(Ns_Entry *entry, void *value, size_t size) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2); -NS_EXTERN void +NS_EXTERN int Ns_CacheSetValueExpires(Ns_Entry *entry, void *value, size_t size, - const Ns_Time *timeoutPtr, int cost, size_t maxSize) + const Ns_Time *timeoutPtr, int cost, size_t maxSize, + uintptr_t transactionEpoch) NS_GNUC_NONNULL(1); NS_EXTERN void @@ -764,10 +799,18 @@ NS_EXTERN Ns_Entry * Ns_CacheFirstEntry(Ns_Cache *cache, Ns_CacheSearch *search) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2); +NS_EXTERN Ns_Entry * +Ns_CacheFirstEntryT(Ns_Cache *cache, Ns_CacheSearch *search, const Ns_CacheTransactionStack *transactionStackPtr) + NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2); + NS_EXTERN Ns_Entry * Ns_CacheNextEntry(Ns_CacheSearch *search) NS_GNUC_NONNULL(1); +NS_EXTERN Ns_Entry * +Ns_CacheNextEntryT(Ns_CacheSearch *search, const Ns_CacheTransactionStack *transactionStackPtr) + NS_GNUC_NONNULL(1); + NS_EXTERN int Ns_CacheFlush(Ns_Cache *cache) NS_GNUC_NONNULL(1); @@ -816,6 +859,10 @@ NS_EXTERN void Ns_CacheSetMaxSize(Ns_Cache *cache, size_t maxSize) NS_GNUC_NONNULL(1); +NS_EXTERN int +Ns_CacheGetNrUncommittedEntries(const Ns_Cache *cache) + NS_GNUC_NONNULL(1); + /* * callbacks.c: */ @@ -859,7 +906,7 @@ Ns_CompressFree(Ns_CompressStream *cStream) NS_EXTERN Ns_ReturnCode Ns_CompressBufsGzip(Ns_CompressStream *cStream, struct iovec *bufs, int nbufs, - Ns_DString *dsPtr, int level, bool flush) + Ns_DString *dsPtr, int level, bool flush) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(4); NS_EXTERN Ns_ReturnCode @@ -1103,9 +1150,6 @@ Ns_ConnSockContent(const Ns_Conn *conn) NS_GNUC_NONNULL(1); NS_EXTERN const char * Ns_ConnDriverName(const Ns_Conn *conn) NS_GNUC_NONNULL(1); -NS_EXTERN void -Ns_ConnSetUrlEncoding(Ns_Conn *conn, Tcl_Encoding encoding) NS_GNUC_NONNULL(1); - NS_EXTERN Ns_ReturnCode Ns_SetConnLocationProc(Ns_ConnLocationProc *proc, void *arg) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2); @@ -1129,7 +1173,7 @@ Ns_ConnFilterTime(Ns_Conn *conn) NS_GNUC_NONNULL(1) NS_GNUC_RETURNS_NONNULL; NS_EXTERN void Ns_ConnTimeSpans(const Ns_Conn *conn, Ns_Time *acceptTimeSpanPtr, Ns_Time *queueTimeSpanPtr, - Ns_Time *filterTimeSpanPtr, Ns_Time *runTimeSpanPtr) + Ns_Time *filterTimeSpanPtr, Ns_Time *runTimeSpanPtr) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(3) NS_GNUC_NONNULL(4) NS_GNUC_NONNULL(5); NS_EXTERN Ns_Time * @@ -1255,7 +1299,7 @@ Ns_ConnSetSecureCookie(const Ns_Conn *conn, const char *name, const char *value, NS_EXTERN void Ns_ConnSetCookieEx(const Ns_Conn *conn, const char *name, const char *value, time_t maxage, - const char *domain, const char *path, unsigned int flags) + const char *domain, const char *path, unsigned int flags) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2); NS_EXTERN void @@ -1392,7 +1436,7 @@ Ns_ExitEventQueue(Ns_EventQueue *queue) NS_EXTERN pid_t Ns_ExecProcess(const char *exec, const char *dir, int fdin, int fdout, - char *args, const Ns_Set *env) + char *args, const Ns_Set *env) NS_GNUC_NONNULL(1); NS_EXTERN pid_t @@ -1401,7 +1445,7 @@ Ns_ExecProc(const char *exec, char **argv) NS_EXTERN pid_t Ns_ExecArgblk(const char *exec, const char *dir, int fdin, int fdout, - char *args, const Ns_Set *env) + char *args, const Ns_Set *env) NS_GNUC_NONNULL(1); NS_EXTERN pid_t @@ -1409,11 +1453,15 @@ Ns_ExecArgv(const char *exec, const char *dir, int fdin, int fdout, char **argv, NS_GNUC_NONNULL(1); NS_EXTERN Ns_ReturnCode -Ns_WaitProcess(pid_t pid); +Ns_WaitProcess(pid_t pid) + NS_GNUC_DEPRECATED_FOR(Ns_WaitForProcessStatus); NS_EXTERN Ns_ReturnCode Ns_WaitForProcess(pid_t pid, int *exitcodePtr); +NS_EXTERN Ns_ReturnCode +Ns_WaitForProcessStatus(pid_t pid, int *exitcodePtr, int *waitstatusPtr); + /* * fastpath.c: */ @@ -1445,7 +1493,7 @@ NS_EXTERN Ns_OpProc Ns_FastPathProc; NS_EXTERN void * Ns_RegisterFilter(const char *server, const char *method, const char *url, - Ns_FilterProc *proc, Ns_FilterType when, void *arg, bool first) + Ns_FilterProc *proc, Ns_FilterType when, void *arg, bool first) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(3) NS_GNUC_NONNULL(4) NS_GNUC_RETURNS_NONNULL; @@ -1480,7 +1528,7 @@ Ns_HtuuDecode(const char *input, unsigned char *buf, size_t bufSize) NS_EXTERN void Ns_IndexInit(Ns_Index *indexPtr, size_t inc, int (*CmpEls) (const void *left, const void *right), - int (*CmpKeyWithEl) (const void *left, const void *right)) + int (*CmpKeyWithEl) (const void *left, const void *right)) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(3) NS_GNUC_NONNULL(4); NS_EXTERN void @@ -1581,7 +1629,7 @@ Ns_ListDeleteLowElements(Ns_List *mPtr, float minweight); NS_EXTERN Ns_List * Ns_ListDeleteWithTest(void *elem, Ns_List *lPtr, - Ns_EqualProc *equalProc); + Ns_EqualProc *equalProc); NS_EXTERN Ns_List * Ns_ListDeleteIf(Ns_List *lPtr, Ns_ElemTestProc *testProc); @@ -1727,8 +1775,8 @@ NS_EXTERN Ns_ObjvProc Ns_ObjvTime; NS_EXTERN Ns_ObjvProc Ns_ObjvWideInt; NS_EXTERN int -Ns_SubcmdObjv(const Ns_SubCmdSpec *subcmdSpec, ClientData clientData, Tcl_Interp *interp, - int objc, Tcl_Obj *CONST* objv) +Ns_SubcmdObjv(const Ns_SubCmdSpec *subcmdSpec, ClientData clientData, + Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(3) NS_GNUC_NONNULL(5); #define Ns_NrElements(arr) ((int) (sizeof(arr) / sizeof((arr)[0]))) @@ -1776,27 +1824,27 @@ Ns_GetTimeFromString(Tcl_Interp *interp, const char *str, Ns_Time *tPtr) NS_EXTERN char * Tcl_DeleteKeyedListField(Tcl_Interp *interp, const char *fieldName, - const char *keyedList); + const char *keyedList); NS_EXTERN int Tcl_GetKeyedListField(Tcl_Interp *interp, const char *fieldName, - const char *keyedList, char **fieldValuePtr); + const char *keyedList, char **fieldValuePtr); NS_EXTERN int Tcl_GetKeyedListKeys(Tcl_Interp *interp, char const *subFieldName, - const char *keyedList, int *keysArgcPtr, char ***keysArgvPtr); + const char *keyedList, int *keysArgcPtr, char ***keysArgvPtr); NS_EXTERN char * Tcl_SetKeyedListField(Tcl_Interp *interp, const char *fieldName, - const char *fieldValue, const char *keyedList); + const char *fieldValue, const char *keyedList); /* * listen.c: */ NS_EXTERN Ns_ReturnCode -Ns_SockListenCallback(const char *addr, unsigned short port, Ns_SockProc *proc, void *arg) - NS_GNUC_NONNULL(3) NS_GNUC_NONNULL(4); +Ns_SockListenCallback(const char *addr, unsigned short port, Ns_SockProc *proc, bool bind, void *arg) + NS_GNUC_NONNULL(3) NS_GNUC_NONNULL(5); NS_EXTERN bool @@ -2031,7 +2079,7 @@ Ns_GetThreadServer(void); NS_EXTERN void Ns_RegisterRequest(const char *server, const char *method, const char *url, Ns_OpProc *proc, Ns_Callback *deleteCallback, void *arg, - int unsigned flags) + int unsigned flags) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(3) NS_GNUC_NONNULL(4); @@ -2248,12 +2296,12 @@ Ns_ConnReturnHtml(Ns_Conn *conn, int status, const char *html, ssize_t len) NS_EXTERN Ns_ReturnCode Ns_ConnReturnCharData(Ns_Conn *conn, int status, const char *data, - ssize_t len, const char *mimeType) + ssize_t len, const char *mimeType) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(3); NS_EXTERN Ns_ReturnCode Ns_ConnReturnData(Ns_Conn *conn, int status, const char *data, - ssize_t len, const char *mimeType) + ssize_t len, const char *mimeType) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(3) NS_GNUC_NONNULL(5); NS_EXTERN Ns_ReturnCode @@ -2414,18 +2462,18 @@ Ns_ScheduleProc(Ns_Callback *proc, void *arg, int thread, int interval) NS_EXTERN int Ns_ScheduleDaily(Ns_SchedProc *proc, void *clientData, unsigned int flags, - int hour, int minute, Ns_SchedProc *cleanupProc) + int hour, int minute, Ns_SchedProc *cleanupProc) NS_GNUC_NONNULL(1); NS_EXTERN int Ns_ScheduleWeekly(Ns_SchedProc *proc, void *clientData, unsigned int flags, - int day, int hour, int minute, - Ns_SchedProc *cleanupProc) + int day, int hour, int minute, + Ns_SchedProc *cleanupProc) NS_GNUC_NONNULL(1); NS_EXTERN int Ns_ScheduleProcEx(Ns_SchedProc *proc, void *clientData, unsigned int flags, - int interval, Ns_SchedProc *cleanupProc) + int interval, Ns_SchedProc *cleanupProc) NS_GNUC_NONNULL(1); NS_EXTERN void @@ -2470,12 +2518,12 @@ Ns_SetUniqueCmp(const Ns_Set *set, const char *key, NS_EXTERN int Ns_SetFindCmp(const Ns_Set *set, const char *key, - int (*cmp) (const char *s1, const char *s2)) + int (*cmp) (const char *s1, const char *s2)) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(3); NS_EXTERN char * Ns_SetGetCmp(const Ns_Set *set, const char *key, - int (*cmp) (const char *s1, const char *s2)) + int (*cmp) (const char *s1, const char *s2)) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(3); NS_EXTERN bool @@ -2695,11 +2743,11 @@ Ns_SockSend(NS_SOCKET sock, const void *buffer, size_t length, const Ns_Time *ti NS_EXTERN ssize_t Ns_SockRecvBufs(NS_SOCKET sock, struct iovec *bufs, int nbufs, - const Ns_Time *timeoutPtr, unsigned int flags); + const Ns_Time *timeoutPtr, unsigned int flags); NS_EXTERN ssize_t Ns_SockSendBufs(Ns_Sock *sockPtr, const struct iovec *bufs, int nbufs, - const Ns_Time *timeoutPtr, unsigned int flags) + const Ns_Time *timeoutPtr, unsigned int flags) NS_GNUC_NONNULL(1); NS_EXTERN NS_SOCKET @@ -2741,7 +2789,7 @@ Ns_SockTimedConnect(const char *host, unsigned short port, const Ns_Time *timeou NS_EXTERN NS_SOCKET Ns_SockTimedConnect2(const char *host, unsigned short port, const char *lhost, unsigned short lport, - const Ns_Time *timeoutPtr) + const Ns_Time *timeoutPtr) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(5); NS_EXTERN Ns_ReturnCode @@ -2907,12 +2955,12 @@ Ns_GetBinaryString(Tcl_Obj *obj, int *lengthPtr) NS_EXTERN Ns_TclCallback * Ns_TclNewCallback(Tcl_Interp *interp, Ns_Callback *cbProc, Tcl_Obj *scriptObjPtr, int objc, - Tcl_Obj *CONST* objv) + Tcl_Obj *CONST* objv) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(3); NS_EXTERN int Ns_TclEvalCallback(Tcl_Interp *interp, const Ns_TclCallback *cbPtr, - Ns_DString *result, ...) NS_GNUC_SENTINEL + Ns_DString *result, ...) NS_GNUC_SENTINEL NS_GNUC_NONNULL(2); @@ -3242,7 +3290,7 @@ Ns_DecodeUrlCharset(Ns_DString *dsPtr, const char *urlSegment, const char *chars NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2); NS_EXTERN void -Ns_UrlEncodingWarnUnencoded(const char *msg, const char *chars) +Ns_UrlEncodingWarnUnencoded(const char *msg, const char *chars) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2); /* @@ -3326,8 +3374,10 @@ NS_EXTERN int ns_pipe(int *fds) NS_GNUC_NONNULL(1); +#ifdef _WIN32 NS_EXTERN int ns_mkstemp(char *charTemplate); +#endif NS_EXTERN int ns_poll(struct pollfd *fds, NS_POLL_NFDS_TYPE nfds, long timo) diff --git a/include/nsthread.h b/include/nsthread.h index 333642d8..976c21de 100644 --- a/include/nsthread.h +++ b/include/nsthread.h @@ -364,9 +364,9 @@ typedef struct DIR_ *DIR; * Many modules use SOCKET and not NS_SOCKET; don't force updates for * the time being, although the use of SOCKET should be deprecated. */ -#ifndef SOCKET -# define SOCKET NS_SOCKET -#endif +# ifndef SOCKET +# define SOCKET NS_SOCKET +# endif typedef int ns_sockerrno_t; @@ -408,28 +408,28 @@ typedef int ns_sockerrno_t; # define _POSIX_PTHREAD_SEMANTICS # endif -#if defined(__APPLE__) || defined(__darwin__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) -# ifndef s6_addr16 -# define s6_addr16 __u6_addr.__u6_addr16 -# endif -# ifndef s6_addr32 -# define s6_addr32 __u6_addr.__u6_addr32 -# endif -# define S6_ADDR16(x) ((uint16_t*)(x).s6_addr16) -# define NS_INITGROUPS_GID_T int -# define NS_MSG_IOVLEN_T int -#else -# if defined(__sun) -# define S6_ADDR16(x) ((uint16_t*)((char*)&(x).s6_addr)) +# if defined(__APPLE__) || defined(__darwin__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +# ifndef s6_addr16 +# define s6_addr16 __u6_addr.__u6_addr16 +# endif # ifndef s6_addr32 -# define s6_addr32 _S6_un._S6_u32 +# define s6_addr32 __u6_addr.__u6_addr32 # endif -# else # define S6_ADDR16(x) ((uint16_t*)(x).s6_addr16) +# define NS_INITGROUPS_GID_T int +# define NS_MSG_IOVLEN_T int +# else +# if defined(__sun) +# define S6_ADDR16(x) ((uint16_t*)((char*)&(x).s6_addr)) +# ifndef s6_addr32 +# define s6_addr32 _S6_un._S6_u32 +# endif +# else +# define S6_ADDR16(x) ((uint16_t*)(x).s6_addr16) +# endif +# define NS_INITGROUPS_GID_T gid_t +# define NS_MSG_IOVLEN_T size_t # endif -# define NS_INITGROUPS_GID_T gid_t -# define NS_MSG_IOVLEN_T size_t -#endif # ifdef __OpenBSD__ # ifndef ENOTSUP @@ -892,7 +892,7 @@ typedef enum { NS_UNAUTHORIZED = (-3), /* authorize result, returned by e.g. Ns_UserAuthorizeProc */ NS_FORBIDDEN = (-4), /* authorize result, returned by e.g. Ns_UserAuthorizeProc */ NS_FILTER_BREAK = (-5), /* filter result, returned by e.g. Ns_FilterProc */ - NS_FILTER_RETURN = (-6), /* filter result, returned by e.g. Ns_FilterProc */ + NS_FILTER_RETURN = (-6) /* filter result, returned by e.g. Ns_FilterProc */ } Ns_ReturnCode; /* @@ -958,6 +958,9 @@ NS_EXTERN void *ns_realloc(void *buf, size_t size) NS_GNUC_WARN_UNUSED_RESULT; NS_EXTERN char *ns_strdup(const char *string) NS_GNUC_NONNULL(1) NS_GNUC_MALLOC NS_GNUC_WARN_UNUSED_RESULT; NS_EXTERN char *ns_strcopy(const char *string) NS_GNUC_MALLOC; NS_EXTERN char *ns_strncopy(const char *string, ssize_t size) NS_GNUC_MALLOC; +NS_EXTERN char *ns_strncopy(const char *string, ssize_t size) NS_GNUC_MALLOC; +NS_EXTERN int ns_uint32toa(char *buffer, uint32_t n) NS_GNUC_NONNULL(1); +NS_EXTERN int ns_uint64toa(char *buffer, uint64_t n) NS_GNUC_NONNULL(1); /* * mutex.c: @@ -1019,7 +1022,7 @@ NS_EXTERN struct tm *ns_localtime(const time_t *clock) NS_GNUC_NONNULL(1); NS_EXTERN struct tm *ns_gmtime(const time_t *clock) NS_GNUC_NONNULL(1); NS_EXTERN char *ns_ctime(const time_t *clock) NS_GNUC_NONNULL(1); NS_EXTERN char *ns_asctime(const struct tm *tmPtr) NS_GNUC_NONNULL(1); -NS_EXTERN char *ns_strtok(char *s1, const char *s2) NS_GNUC_NONNULL(1); +NS_EXTERN char *ns_strtok(char *s1, const char *s2) NS_GNUC_NONNULL(2); NS_EXTERN char *ns_inet_ntoa(struct sockaddr *saPtr) NS_GNUC_NONNULL(1); /* diff --git a/nscgi/nscgi.c b/nscgi/nscgi.c index 019aa948..f0c06f98 100644 --- a/nscgi/nscgi.c +++ b/nscgi/nscgi.c @@ -213,7 +213,7 @@ Ns_ModuleInit(const char *server, const char *module) Ns_Log(Warning, "nscgi: no such interps section: %s", ds.string); } - Ns_DStringTrunc(&ds, 0); + Ns_DStringSetLength(&ds, 0); } section = Ns_ConfigGetValue(path, "environment"); if (section != NULL) { @@ -223,7 +223,7 @@ Ns_ModuleInit(const char *server, const char *module) Ns_Log(Warning, "nscgi: no such environment section: %s", ds.string); } - Ns_DStringTrunc(&ds, 0); + Ns_DStringSetLength(&ds, 0); } if (Ns_ConfigBool(path, "systemenvironment", NS_FALSE)) { modPtr->flags |= CGI_SYSENV; @@ -702,7 +702,7 @@ CgiFree(Cgi *cgiPtr) * Reap the process. */ - if (cgiPtr->pid != NS_INVALID_PID && Ns_WaitProcess(cgiPtr->pid) != NS_OK) { + if (cgiPtr->pid != NS_INVALID_PID && Ns_WaitForProcessStatus(cgiPtr->pid, NULL, NULL) != NS_OK) { Ns_Log(Error, "nscgi: wait for %s failed: %s", cgiPtr->exec, strerror(errno)); } @@ -784,7 +784,7 @@ CgiExec(Cgi *cgiPtr, Ns_Conn *conn) } ++envp; } - Ns_DStringTrunc(dsPtr, 0); + Ns_DStringSetLength(dsPtr, 0); } /* @@ -812,7 +812,7 @@ CgiExec(Cgi *cgiPtr, Ns_Conn *conn) } else { Ns_SetUpdate(cgiPtr->env, "PATH_INFO", cgiPtr->pathinfo); } - Ns_DStringTrunc(dsPtr, 0); + Ns_DStringSetLength(dsPtr, 0); Ns_DStringInit(&tmp); (void)Ns_UrlToFile(dsPtr, modPtr->server, cgiPtr->pathinfo); if (Ns_UrlPathDecode(&tmp, dsPtr->string, NULL) != NULL) { @@ -821,17 +821,17 @@ CgiExec(Cgi *cgiPtr, Ns_Conn *conn) Ns_SetUpdate(cgiPtr->env, "PATH_TRANSLATED", dsPtr->string); } Ns_DStringFree(&tmp); - Ns_DStringTrunc(dsPtr, 0); + Ns_DStringSetLength(dsPtr, 0); } else { Ns_SetUpdate(cgiPtr->env, "PATH_INFO", ""); } Ns_SetUpdate(cgiPtr->env, "GATEWAY_INTERFACE", "CGI/1.1"); Ns_DStringVarAppend(dsPtr, Ns_InfoServerName(), "/", Ns_InfoServerVersion(), (char *)0L); Ns_SetUpdate(cgiPtr->env, "SERVER_SOFTWARE", dsPtr->string); - Ns_DStringTrunc(dsPtr, 0); + Ns_DStringSetLength(dsPtr, 0); Ns_DStringPrintf(dsPtr, "HTTP/%2.1f", conn->request.version); Ns_SetUpdate(cgiPtr->env, "SERVER_PROTOCOL", dsPtr->string); - Ns_DStringTrunc(dsPtr, 0); + Ns_DStringSetLength(dsPtr, 0); /* * Determine SERVER_NAME and SERVER_PORT from the conn location. @@ -850,14 +850,14 @@ CgiExec(Cgi *cgiPtr, Ns_Conn *conn) for (j = 0; *p != '\0'; ++p, ++j) { ; } - Ns_DStringTrunc(dsPtr, j); + Ns_DStringSetLength(dsPtr, j); } Ns_SetUpdate(cgiPtr->env, "SERVER_NAME", dsPtr->string); - Ns_DStringTrunc(dsPtr, 0); + Ns_DStringSetLength(dsPtr, 0); if (p == NULL) { Ns_DStringPrintf(dsPtr, "%hu", Ns_ConnPort(conn)); Ns_SetUpdate(cgiPtr->env, "SERVER_PORT", dsPtr->string); - Ns_DStringTrunc(dsPtr, 0); + Ns_DStringSetLength(dsPtr, 0); } /* @@ -876,7 +876,7 @@ CgiExec(Cgi *cgiPtr, Ns_Conn *conn) if (Ns_GetHostByAddr(dsPtr, peer) == NS_TRUE) { Ns_SetUpdate(cgiPtr->env, "REMOTE_HOST", dsPtr->string); } - Ns_DStringTrunc(dsPtr, 0); + Ns_DStringSetLength(dsPtr, 0); } else { Ns_SetUpdate(cgiPtr->env, "REMOTE_HOST", peer); } @@ -905,7 +905,7 @@ CgiExec(Cgi *cgiPtr, Ns_Conn *conn) } else { Ns_DStringPrintf(dsPtr, "%u", (unsigned) conn->contentLength); Ns_SetUpdate(cgiPtr->env, "CONTENT_LENGTH", dsPtr->string); - Ns_DStringTrunc(dsPtr, 0); + Ns_DStringSetLength(dsPtr, 0); } /* @@ -934,14 +934,14 @@ CgiExec(Cgi *cgiPtr, Ns_Conn *conn) } else { SetAppend(cgiPtr->env, index, ", ", e); } - Ns_DStringTrunc(dsPtr, 5); + Ns_DStringSetLength(dsPtr, 5); } /* * Build up the argument block. */ - Ns_DStringTrunc(dsPtr, 0); + Ns_DStringSetLength(dsPtr, 0); if (cgiPtr->interp != NULL) { Ns_DStringAppendArg(dsPtr, cgiPtr->interp); } @@ -1071,7 +1071,7 @@ CgiReadLine(Cgi *cgiPtr, Ns_DString *dsPtr) if (c == '\n') { while (dsPtr->length > 0 && CHARTYPE(space, dsPtr->string[dsPtr->length - 1]) != 0) { - Ns_DStringTrunc(dsPtr, dsPtr->length-1); + Ns_DStringSetLength(dsPtr, dsPtr->length-1); } return (ssize_t)dsPtr->length; } @@ -1160,7 +1160,7 @@ CgiCopy(Cgi *cgiPtr, Ns_Conn *conn) last = (int)Ns_SetPut(hdrs, ds.string, value); } } - Ns_DStringTrunc(&ds, 0); + Ns_DStringSetLength(&ds, 0); } Ns_DStringFree(&ds); if (n < 0) { diff --git a/nscp/nscp.c b/nscp/nscp.c index ff61454f..3b47612c 100644 --- a/nscp/nscp.c +++ b/nscp/nscp.c @@ -409,7 +409,7 @@ EvalThread(void *arg) Tcl_DStringInit(&unameDS); Ns_DStringPrintf(&ds, "-nscp:%d-", sessPtr->id); Ns_ThreadSetName(ds.string); - Tcl_DStringTrunc(&ds, 0); + Tcl_DStringSetLength(&ds, 0); Ns_Log(Notice, "nscp: %s connected", ns_inet_ntop((struct sockaddr *)&(sessPtr->sa), ipString, sizeof(ipString))); @@ -437,7 +437,7 @@ EvalThread(void *arg) while (stop == 0) { char buf[64]; - Tcl_DStringTrunc(&ds, 0); + Tcl_DStringSetLength(&ds, 0); ++ncmd; retry: snprintf(buf, sizeof(buf), "%s:nscp %d> ", server, ncmd); @@ -451,7 +451,7 @@ EvalThread(void *arg) snprintf(buf, sizeof(buf), "%s:nscp %d>>> ", server, ncmd); } while (ds.length > 0 && ds.string[ds.length-1] == '\n') { - Tcl_DStringTrunc(&ds, ds.length-1); + Tcl_DStringSetLength(&ds, ds.length-1); } if (STREQ(ds.string, "")) { goto retry; /* Empty command - try again. */ diff --git a/nsd/adpcmds.c b/nsd/adpcmds.c index 8fb9a60f..1988ede7 100644 --- a/nsd/adpcmds.c +++ b/nsd/adpcmds.c @@ -824,7 +824,7 @@ NsTclAdpTruncObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj if (GetOutput(clientData, &dsPtr) != TCL_OK) { result = TCL_ERROR; } else { - Ns_DStringTrunc(dsPtr, length); + Ns_DStringSetLength(dsPtr, length); } } return result; diff --git a/nsd/adpeval.c b/nsd/adpeval.c index f8418fd1..8f3acd92 100644 --- a/nsd/adpeval.c +++ b/nsd/adpeval.c @@ -398,7 +398,7 @@ NsAdpReset(NsInterp *itPtr) itPtr->adp.bufsize = 1024u * 1000u; itPtr->adp.flags = 0u; } - Tcl_DStringTrunc(&itPtr->adp.output, 0); + Tcl_DStringSetLength(&itPtr->adp.output, 0); } /* @@ -462,7 +462,7 @@ AdpSource(NsInterp *itPtr, int objc, Tcl_Obj *CONST* objv, const char *file, } } file = Ns_NormalizePath(&path, file); - Ns_DStringTrunc(&tmp, 0); + Ns_DStringSetLength(&tmp, 0); /* * Check for TclPro debugging. @@ -493,7 +493,7 @@ AdpSource(NsInterp *itPtr, int objc, Tcl_Obj *CONST* objv, const char *file, Ns_DStringPrintf(&tmp, "nsadp:%p", (void *)itPtr); itPtr->adp.cache = Ns_CacheCreateSz(tmp.string, TCL_STRING_KEYS, itPtr->servPtr->adp.cachesize, FreeInterpPage); - Ns_DStringTrunc(&tmp, 0); + Ns_DStringSetLength(&tmp, 0); } /* @@ -653,7 +653,7 @@ AdpSource(NsInterp *itPtr, int objc, Tcl_Obj *CONST* objv, const char *file, Ns_IncrTime(&cachePtr->expires, expiresPtr->sec, expiresPtr->usec); cachePtr->refcnt = 1; } - Ns_DStringTrunc(&tmp, 0); + Ns_DStringSetLength(&tmp, 0); Ns_MutexLock(&servPtr->adp.pagelock); if (cachePtr != NULL) { if (pagePtr->cachePtr != NULL) { @@ -1019,7 +1019,7 @@ NsAdpLogError(NsInterp *itPtr) } err = Ns_TclLogErrorInfo(interp, ds.string); if ((itPtr->adp.flags & ADP_DISPLAY) != 0u) { - Ns_DStringTrunc(&ds, 0); + Ns_DStringSetLength(&ds, 0); Ns_DStringAppend(&ds, "
\n");
         Ns_QuoteHtml(&ds, err);
         Ns_DStringAppend(&ds, "\n
\n"); @@ -1263,7 +1263,7 @@ AdpDebug(const NsInterp *itPtr, const char *ptr, int len, int nscript) debugfile, Tcl_PosixError(interp)); result = TCL_ERROR; } else { - Ns_DStringTrunc(&ds, 0); + Ns_DStringSetLength(&ds, 0); Ns_DStringVarAppend(&ds, "source ", debugfile, (char *)0); result = Tcl_EvalEx(interp, ds.string, ds.length, 0); } diff --git a/nsd/adpparse.c b/nsd/adpparse.c index 27f29b1c..5cee0f52 100644 --- a/nsd/adpparse.c +++ b/nsd/adpparse.c @@ -678,10 +678,9 @@ AppendBlock(Parse *parsePtr, const char *s, char *e, char type, unsigned int fla /* * Increment line numbers based on the passed-in segment */ - while (likely(s < e)) { - if (unlikely(*s++ == '\n')) { - ++parsePtr->line; - } + while( ((s = strchr(s, '\n')) != NULL) && (s < e)) { + ++parsePtr->line; + ++s; } } } @@ -721,7 +720,7 @@ GetTag(Tcl_DString *dsPtr, char *s, const char *e, char **aPtr) while (s < e && CHARTYPE(space, *s) == 0) { ++s; } - Tcl_DStringTrunc(dsPtr, 0); + Tcl_DStringSetLength(dsPtr, 0); Tcl_DStringAppend(dsPtr, t, (int)(s - t)); if (aPtr != NULL) { while (s < e && CHARTYPE(space, *s) != 0) { diff --git a/nsd/adprequest.c b/nsd/adprequest.c index 74470d41..bc613184 100644 --- a/nsd/adprequest.c +++ b/nsd/adprequest.c @@ -606,7 +606,7 @@ NsAdpFlush(NsInterp *itPtr, bool doStream) itPtr->adp.exception = ADP_ABORT; } } - Tcl_DStringTrunc(&itPtr->adp.output, 0); + Tcl_DStringSetLength(&itPtr->adp.output, 0); if (!doStream) { NsAdpReset(itPtr); diff --git a/nsd/auth.c b/nsd/auth.c index 8d3193de..2e7aecba 100644 --- a/nsd/auth.c +++ b/nsd/auth.c @@ -135,7 +135,6 @@ int NsTclRequestAuthorizeObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) { const NsInterp *itPtr = clientData; - Ns_ReturnCode status; int result = TCL_OK; char *method, *url, *authuser, *authpasswd, *ipaddr = NULL; Ns_ObjvSpec args[] = { @@ -151,6 +150,8 @@ NsTclRequestAuthorizeObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, result = TCL_ERROR; } else { + Ns_ReturnCode status; + status = Ns_AuthorizeRequest(itPtr->servPtr->server, method, url, authuser, authpasswd, ipaddr); switch (status) { diff --git a/nsd/cache.c b/nsd/cache.c index b5f53d4f..910489ec 100644 --- a/nsd/cache.c +++ b/nsd/cache.c @@ -49,11 +49,13 @@ typedef struct Entry { struct Entry *prevPtr; struct Cache *cachePtr; Tcl_HashEntry *hPtr; - Ns_Time expires; /* Absolute ttl timeout. */ + Ns_Time expires; /* Absolute ttl timeout. */ size_t size; - int cost; /* cost to compute a single entry */ - int count; /* reuse of this entry */ - void *value; /* Will appear NULL for concurrent updates. */ + int cost; /* cost to compute a single entry */ + int count; /* reuse count of this entry */ + void *value; /* Will appear NULL for concurrent updates. */ + void *uncommittedValue; /* Used for transactional mode */ + uintptr_t transactionEpoch; /* Used for identifying transaction */ } Entry; /* @@ -70,13 +72,16 @@ typedef struct Cache { Ns_Mutex lock; Ns_Cond cond; Tcl_HashTable entriesTable; - + uintptr_t transactionEpoch; + Tcl_HashTable uncommittedTable; struct { unsigned long nhit; /* Successful gets. */ unsigned long nmiss; /* Unsuccessful gets. */ unsigned long nexpired; /* Unsuccessful gets due to entry expiry. */ unsigned long nflushed; /* Explicit flushes by user code. */ unsigned long npruned; /* Evictions due to size constraint. */ + unsigned long ncommit; /* number of commits. */ + unsigned long nrollback; /* number of rollback operations. */ } stats; char name[1]; @@ -97,6 +102,10 @@ static void Delink(Entry *ePtr) static void Push(Entry *ePtr) NS_GNUC_NONNULL(1); +static unsigned long +CacheTransaction(Cache *cachePtr, uintptr_t epoch, bool commit) + NS_GNUC_NONNULL(1); + /* *---------------------------------------------------------------------- @@ -127,19 +136,22 @@ Ns_CacheCreateSz(const char *name, int keys, size_t maxSize, Ns_Callback *freePr cachePtr = ns_calloc(1u, sizeof(Cache) + nameLength); memcpy(cachePtr->name, name, nameLength + 1u); - cachePtr->freeProc = freeProc; - cachePtr->maxSize = maxSize; - cachePtr->currentSize = 0u; - cachePtr->keys = keys; - cachePtr->stats.nhit = 0u; - cachePtr->stats.nmiss = 0u; - cachePtr->stats.nexpired = 0u; - cachePtr->stats.nflushed = 0u; - cachePtr->stats.npruned = 0u; + cachePtr->freeProc = freeProc; + cachePtr->maxSize = maxSize; + cachePtr->currentSize = 0u; + cachePtr->keys = keys; + cachePtr->stats.nhit = 0u; + cachePtr->stats.nmiss = 0u; + cachePtr->stats.nexpired = 0u; + cachePtr->stats.nflushed = 0u; + cachePtr->stats.npruned = 0u; + cachePtr->stats.ncommit = 0u; + cachePtr->stats.nrollback = 0u; Ns_MutexInit(&cachePtr->lock); Ns_MutexSetName2(&cachePtr->lock, "ns:cache", name); Tcl_InitHashTable(&cachePtr->entriesTable, keys); + Tcl_InitHashTable(&cachePtr->uncommittedTable, TCL_ONE_WORD_KEYS); return (Ns_Cache *) cachePtr; } @@ -172,6 +184,7 @@ Ns_CacheDestroy(Ns_Cache *cache) Ns_MutexDestroy(&cachePtr->lock); Ns_CondDestroy(&cachePtr->cond); Tcl_DeleteHashTable(&cachePtr->entriesTable); + Tcl_DeleteHashTable(&cachePtr->uncommittedTable); ns_free(cachePtr); } @@ -192,9 +205,14 @@ Ns_CacheDestroy(Ns_Cache *cache) * *---------------------------------------------------------------------- */ - Ns_Entry * Ns_CacheFindEntry(Ns_Cache *cache, const char *key) +{ + return Ns_CacheFindEntryT(cache, key, NULL); +} + +Ns_Entry * +Ns_CacheFindEntryT(Ns_Cache *cache, const char *key, const Ns_CacheTransactionStack *transactionStackPtr) { Cache *cachePtr = (Cache *) cache; const Tcl_HashEntry *hPtr; @@ -204,7 +222,7 @@ Ns_CacheFindEntry(Ns_Cache *cache, const char *key) NS_NONNULL_ASSERT(key != NULL); hPtr = Tcl_FindHashEntry(&cachePtr->entriesTable, key); - if (hPtr == NULL) { + if (unlikely(hPtr == NULL)) { /* * Entry does not exist at all. */ @@ -214,14 +232,16 @@ Ns_CacheFindEntry(Ns_Cache *cache, const char *key) } else { Entry *ePtr = Tcl_GetHashValue(hPtr); - if (ePtr->value == NULL) { + if (unlikely(ePtr->value == NULL + && (transactionStackPtr == NULL || transactionStackPtr->depth == 0) + )) { /* * Entry is being updated by some other thread. */ ++cachePtr->stats.nmiss; result = NULL; - } else if (Expired(ePtr, NULL) == NS_TRUE) { + } else if (unlikely(Expired(ePtr, NULL))) { /* * Entry exists but has expired. */ @@ -230,14 +250,28 @@ Ns_CacheFindEntry(Ns_Cache *cache, const char *key) result = NULL; } else { - /* - * Entry is valid. - */ - ++cachePtr->stats.nhit; - Delink(ePtr); - ePtr->count ++; - Push(ePtr); - result = (Ns_Entry *) ePtr; + void *value; + + if (ePtr->value == NULL) { + value = Ns_CacheGetValueT((Ns_Entry *) ePtr, transactionStackPtr); + if (value == NULL) { + ++cachePtr->stats.nmiss; + } + } else { + value = ePtr->value; + } + if (value != NULL) { + /* + * Entry is valid. + */ + ++cachePtr->stats.nhit; + Delink(ePtr); + ePtr->count ++; + Push(ePtr); + result = (Ns_Entry *) ePtr; + } else { + result = NULL; + } } } return result; @@ -282,7 +316,7 @@ Ns_CacheCreateEntry(Ns_Cache *cache, const char *key, int *newPtr) ++cachePtr->stats.nmiss; } else { ePtr = Tcl_GetHashValue(hPtr); - if (Expired(ePtr, NULL) == NS_TRUE) { + if (Expired(ePtr, NULL)) { ++cachePtr->stats.nexpired; Ns_CacheUnsetValue((Ns_Entry *) ePtr); isNew = 1; @@ -316,10 +350,16 @@ Ns_CacheCreateEntry(Ns_Cache *cache, const char *key, int *newPtr) * *---------------------------------------------------------------------- */ - Ns_Entry * Ns_CacheWaitCreateEntry(Ns_Cache *cache, const char *key, int *newPtr, const Ns_Time *timeoutPtr) +{ + return Ns_CacheWaitCreateEntryT(cache, key, newPtr, timeoutPtr, NULL); +} + +Ns_Entry * +Ns_CacheWaitCreateEntryT(Ns_Cache *cache, const char *key, int *newPtr, + const Ns_Time *timeoutPtr, const Ns_CacheTransactionStack *transactionStackPtr) { Ns_Entry *entry; int isNew; @@ -330,13 +370,13 @@ Ns_CacheWaitCreateEntry(Ns_Cache *cache, const char *key, int *newPtr, NS_NONNULL_ASSERT(newPtr != NULL); entry = Ns_CacheCreateEntry(cache, key, &isNew); - if (isNew == 0 && Ns_CacheGetValue(entry) == NULL) { + if (isNew == 0 && Ns_CacheGetValueT(entry, transactionStackPtr) == NULL) { do { status = Ns_CacheTimedWait(cache, timeoutPtr); entry = Ns_CacheCreateEntry(cache, key, &isNew); } while (status == NS_OK && isNew == 0 - && Ns_CacheGetValue(entry) == NULL); + && Ns_CacheGetValueT(entry, transactionStackPtr) == NULL); } *newPtr = isNew; @@ -375,9 +415,10 @@ Ns_CacheKey(const Ns_Entry *entry) /* *---------------------------------------------------------------------- * - * Ns_CacheGetValue, Ns_CacheGetSize, Ns_CacheGetExpirey -- + * Ns_CacheGetValue, Ns_CacheGetSize, Ns_CacheGetExpirey, + * Ns_CacheGetTransactionEpoch -- * - * Get the value, size or expirey of a cache entry. + * Get the bare components of a cache entry via API. * * Results: * As specified. @@ -409,6 +450,95 @@ Ns_CacheGetExpirey(const Ns_Entry *entry) return &((const Entry *) entry)->expires; } +uintptr_t +Ns_CacheGetTransactionEpoch(const Ns_Entry *entry) +{ + NS_NONNULL_ASSERT(entry != NULL); + return ((const Entry *) entry)->transactionEpoch; +} + + +/* + *---------------------------------------------------------------------- + * + * Ns_CacheGetValueT -- + * + * Get the value a cache entry, respecting the cache transaction + * stack. The functio returns either the bare stack value (if present) or + * the uncommitted value, when called from within a cache transaction. + * + * Results: + * The cache value or NULL + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ +void * +Ns_CacheGetValueT(const Ns_Entry *entry, const Ns_CacheTransactionStack *transactionStackPtr) +{ + const Entry *e; + void *result; + + NS_NONNULL_ASSERT(entry != NULL); + + e = (const Entry *)entry; + /* + * In case, there is a value, we are sure this is a committed value. + */ + if (e->value != NULL) { + result = e->value; + } else { + /* + * If there is no value, it might be an ongoing ns_cache_eval in + * another thread or there might be an uncommitted value from the same + * or another thread. In these cases this function might return NULL. + */ + + result = NULL; + if (transactionStackPtr != NULL) { + unsigned int i; + + for (i = 0; i < transactionStackPtr->depth; i++) { + if (e->transactionEpoch == transactionStackPtr->stack[i]) { + result = e->uncommittedValue; + break; + } + } + } + } + return result; +} + + + +/* + *---------------------------------------------------------------------- + * + * Ns_CacheGetNrUncommittedEntries -- + * + * Return for a given cache the number of uncommitted entries. + * + * Results: + * positive integer, type align with Tcl Hash Tables + * + * Side effects: + * Nonoe. + * + *---------------------------------------------------------------------- + */ +int +Ns_CacheGetNrUncommittedEntries(const Ns_Cache *cache) +{ + const Cache *cachePtr; + + NS_NONNULL_ASSERT(cache != NULL); + + cachePtr = (const Cache *)cache; + return cachePtr->uncommittedTable.numEntries; +} + /* *---------------------------------------------------------------------- @@ -441,16 +571,17 @@ Ns_CacheSetValueSz(Ns_Entry *entry, void *value, size_t size) { NS_NONNULL_ASSERT(entry != NULL); NS_NONNULL_ASSERT(value != NULL); - Ns_CacheSetValueExpires(entry, value, size, NULL, 0, 0u); + (void)Ns_CacheSetValueExpires(entry, value, size, NULL, 0, 0u, 0u); } -void +int Ns_CacheSetValueExpires(Ns_Entry *entry, void *value, size_t size, - const Ns_Time *timeoutPtr, int cost, - size_t maxSize) + const Ns_Time *timeoutPtr, int cost, size_t maxSize, + uintptr_t transactionEpoch) { Entry *ePtr; Cache *cachePtr; + int result; NS_NONNULL_ASSERT(entry != NULL); @@ -458,7 +589,24 @@ Ns_CacheSetValueExpires(Ns_Entry *entry, void *value, size_t size, cachePtr = ePtr->cachePtr; Ns_CacheUnsetValue(entry); - ePtr->value = value; + + if (transactionEpoch == 0) { + ePtr->value = value; + result = 0; + } else { + int isNew; + + ePtr->uncommittedValue = value; + ePtr->transactionEpoch = transactionEpoch; + + (void) Tcl_CreateHashEntry(&cachePtr->uncommittedTable, ePtr, &isNew); + if (unlikely(isNew == 0)) { + Ns_Log(Warning, "cache %s: adding entry %p with value '%s' multiple times to pending table", + ePtr->cachePtr->name, (void *)ePtr, (char *)value); + } + + result = 1; + } ePtr->size = size; ePtr->cost = cost; ePtr->count = 1; @@ -479,9 +627,9 @@ Ns_CacheSetValueExpires(Ns_Entry *entry, void *value, size_t size, */ cachePtr->maxSize = maxSize; } - + if (maxSize > 0u) { - /* + /* * Make space for the new entry, but don't delete the current * entry, and don't delete other newborn entries (with a value * of NULL) of some other threads which are concurrently @@ -495,6 +643,7 @@ Ns_CacheSetValueExpires(Ns_Entry *entry, void *value, size_t size, ++cachePtr->stats.npruned; } } + return result; } @@ -519,15 +668,14 @@ void Ns_CacheUnsetValue(Ns_Entry *entry) { Entry *ePtr; - void *value; NS_NONNULL_ASSERT(entry != NULL); ePtr = (Entry *) entry; - value = ePtr->value; - if (value != NULL) { + if (ePtr->value != NULL || ePtr->uncommittedValue != NULL) { Cache *cachePtr; + void *value; /* * In case, the freeProc() wants to allocate itself @@ -537,12 +685,19 @@ Ns_CacheUnsetValue(Ns_Entry *entry) * ePtr->value to NULL before it is actually deallocated and * call the freeProc after updating all entry members. */ + if (likely(ePtr->value != NULL)) { + value = ePtr->value; + ePtr->value = NULL; + } else { + value = ePtr->uncommittedValue; + ePtr->uncommittedValue = NULL; + } + cachePtr = ePtr->cachePtr; cachePtr->currentSize -= ePtr->size; ePtr->size = 0u; - ePtr->value = NULL; ePtr->expires.sec = ePtr->expires.usec = 0; - + if (cachePtr->freeProc != NULL) { (*cachePtr->freeProc)(value); } @@ -583,7 +738,8 @@ Ns_CacheFlushEntry(Ns_Entry *entry) void Ns_CacheDeleteEntry(Ns_Entry *entry) { - Entry *ePtr; + Entry *ePtr; + Tcl_HashEntry *hPtr; NS_NONNULL_ASSERT(entry != NULL); @@ -591,6 +747,12 @@ Ns_CacheDeleteEntry(Ns_Entry *entry) Ns_CacheUnsetValue(entry); Delink(ePtr); Tcl_DeleteHashEntry(ePtr->hPtr); + + hPtr = Tcl_FindHashEntry(&ePtr->cachePtr->uncommittedTable, ePtr); + if (unlikely(hPtr != NULL)) { + Tcl_DeleteHashEntry(hPtr); + } + ns_free(ePtr); } @@ -650,9 +812,14 @@ Ns_CacheFlush(Ns_Cache *cache) * *---------------------------------------------------------------------- */ - Ns_Entry * Ns_CacheFirstEntry(Ns_Cache *cache, Ns_CacheSearch *search) +{ + return Ns_CacheFirstEntryT(cache, search, NULL); +} + +Ns_Entry * +Ns_CacheFirstEntryT(Ns_Cache *cache, Ns_CacheSearch *search, const Ns_CacheTransactionStack *transactionStackPtr) { Cache *cachePtr = (Cache *) cache; const Tcl_HashEntry *hPtr; @@ -666,8 +833,8 @@ Ns_CacheFirstEntry(Ns_Cache *cache, Ns_CacheSearch *search) while (hPtr != NULL) { Ns_Entry *entry = Tcl_GetHashValue(hPtr); - if (Ns_CacheGetValue(entry) != NULL) { - if (Expired((Entry *) entry, &search->now) == NS_FALSE) { + if (Ns_CacheGetValueT(entry, transactionStackPtr) != NULL) { + if (!Expired((Entry *) entry, &search->now)) { result = entry; break; } @@ -679,6 +846,120 @@ Ns_CacheFirstEntry(Ns_Cache *cache, Ns_CacheSearch *search) return result; } + + +/* + *---------------------------------------------------------------------- + * + * Ns_CacheCommitEntries -- + * + * Commit all cache entries at the end of a successful transaction. + * + * Results: + * number of committed entries. + * + * Side effects: + * Storing value like for transaction less cache entries. + * + *---------------------------------------------------------------------- + */ +unsigned long +Ns_CacheCommitEntries(Ns_Cache *cache, uintptr_t epoch) +{ + unsigned long result; + Cache *cachePtr; + + NS_NONNULL_ASSERT(cache != NULL); + + cachePtr = (Cache *) cache; + result = CacheTransaction(cachePtr, epoch, NS_TRUE); + cachePtr->stats.ncommit += result; + + return result; +} + + +/* + *---------------------------------------------------------------------- + * + * Ns_CacheRollbackEntries -- + * + * Rollback all cache entries at the end of a successful transaction. + * + * Results: + * Number of rolled-back entries. + * + * Side effects: + * Freeing unneeded entries. + * + *---------------------------------------------------------------------- + */ +unsigned long +Ns_CacheRollbackEntries(Ns_Cache *cache, uintptr_t epoch) +{ + unsigned long result; + Cache *cachePtr; + + NS_NONNULL_ASSERT(cache != NULL); + + cachePtr = (Cache *) cache; + result = CacheTransaction(cachePtr, epoch, NS_FALSE); + cachePtr->stats.nrollback += result; + + return result; +} + +/* + *---------------------------------------------------------------------- + * + * CacheTransaction -- + * + * Helper function to iterate over caches and either commit or roll back + * write operations in the caches. + * + * Results: + * Number of modified entries. + * + * Side effects: + * Potentially freeing unneeded entries. + * + *---------------------------------------------------------------------- + */ +static unsigned long +CacheTransaction(Cache *cachePtr, uintptr_t epoch, bool commit) +{ + Ns_CacheSearch search; + Tcl_HashEntry *hPtr; + unsigned long count = 0u; + + NS_NONNULL_ASSERT(cachePtr != NULL); + + hPtr = Tcl_FirstHashEntry(&cachePtr->uncommittedTable, &search.hsearch); + while (hPtr != NULL) { + Ns_Entry *entry = Tcl_GetHashKey(&cachePtr->uncommittedTable, hPtr); + Entry *e = (Entry *) entry; + + if (e->value == NULL && e->transactionEpoch == epoch) { + + if (commit) { + e->value = e->uncommittedValue; + e->uncommittedValue = NULL; + e->transactionEpoch = 0u; + + Tcl_DeleteHashEntry(hPtr); + } else { + /* + * Ns_CacheDeleteEntry() will try to delete the same hPtr of above. + */ + Ns_CacheDeleteEntry(entry); + } + count ++; + } + hPtr = Tcl_NextHashEntry(&search.hsearch); + } + return count; +} + /* *---------------------------------------------------------------------- @@ -699,6 +980,12 @@ Ns_CacheFirstEntry(Ns_Cache *cache, Ns_CacheSearch *search) Ns_Entry * Ns_CacheNextEntry(Ns_CacheSearch *search) +{ + return Ns_CacheNextEntryT(search, NULL); +} + +Ns_Entry * +Ns_CacheNextEntryT(Ns_CacheSearch *search, const Ns_CacheTransactionStack *transactionStackPtr) { const Tcl_HashEntry *hPtr; Ns_Entry *result = NULL; @@ -709,8 +996,8 @@ Ns_CacheNextEntry(Ns_CacheSearch *search) while (hPtr != NULL) { Ns_Entry *entry = Tcl_GetHashValue(hPtr); - if (Ns_CacheGetValue(entry) != NULL) { - if (Expired((Entry *) entry, &search->now) == NS_FALSE) { + if (Ns_CacheGetValueT(entry, transactionStackPtr) != NULL) { + if (!Expired((Entry *) entry, &search->now)) { result = entry; break; } @@ -932,12 +1219,13 @@ Ns_CacheStats(Ns_Cache *cache, Ns_DString *dest) return Ns_DStringPrintf(dest, "maxsize %lu size %lu entries %d " "flushed %lu hits %lu missed %lu hitrate %lu " - "expired %lu pruned %lu saved %.6f", + "expired %lu pruned %lu commit %lu rollback %lu saved %.6f", (unsigned long) cachePtr->maxSize, (unsigned long) cachePtr->currentSize, cachePtr->entriesTable.numEntries, cachePtr->stats.nflushed, cachePtr->stats.nhit, cachePtr->stats.nmiss, hitrate, cachePtr->stats.nexpired, cachePtr->stats.npruned, + cachePtr->stats.ncommit, cachePtr->stats.nrollback, savedCost); } @@ -988,7 +1276,7 @@ void Ns_CacheSetMaxSize(Ns_Cache *cache, size_t maxSize) { NS_NONNULL_ASSERT(cache != NULL); - + ((Cache *) cache)->maxSize = maxSize; } @@ -996,10 +1284,11 @@ size_t Ns_CacheGetMaxSize(const Ns_Cache *cache) { NS_NONNULL_ASSERT(cache != NULL); - + return ((const Cache *) cache)->maxSize; } + /* *---------------------------------------------------------------------- diff --git a/nsd/config.c b/nsd/config.c index 76b29efc..2cb0c453 100644 --- a/nsd/config.c +++ b/nsd/config.c @@ -387,6 +387,7 @@ Ns_ConfigGetInt(const char *section, const char *key, int *valuePtr) NS_NONNULL_ASSERT(section != NULL); NS_NONNULL_ASSERT(key != NULL); + NS_NONNULL_ASSERT(valuePtr != NULL); s = ConfigGet(section, key, NS_FALSE, NULL); if (s != NULL && Ns_StrToInt(s, valuePtr) == NS_OK) { @@ -426,6 +427,10 @@ Ns_ConfigGetInt64(const char *section, const char *key, int64_t *valuePtr) const char *s; bool success = NS_TRUE; + NS_NONNULL_ASSERT(section != NULL); + NS_NONNULL_ASSERT(key != NULL); + NS_NONNULL_ASSERT(valuePtr != NULL); + s = Ns_ConfigGetValue(section, key); if (s == NULL || sscanf(s, "%24" SCNd64, valuePtr) != 1) { success = NS_FALSE; @@ -455,7 +460,7 @@ bool Ns_ConfigGetBool(const char *section, const char *key, bool *valuePtr) { const char *s; - bool found = NS_FALSE; + bool found = NS_FALSE; NS_NONNULL_ASSERT(section != NULL); NS_NONNULL_ASSERT(key != NULL); @@ -792,6 +797,9 @@ ParamObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* } else { Ns_Set *set = *((Ns_Set **) clientData); + + assert(paramName != NULL); + assert(paramValue != NULL); if (likely(set != NULL)) { (void)Ns_SetPut(set, paramName, paramValue); @@ -903,9 +911,10 @@ ConfigGet(const char *section, const char *key, bool exact, const char *defstr) * * Results: * Pointer to new or existing Ns_Set for given section. + * When "create" is not set, the function might return NULL. * * Side effects: - * Section set created (if necessary). + * Section set created (if necessary and "create" is given as true). * *---------------------------------------------------------------------- */ diff --git a/nsd/conn.c b/nsd/conn.c index 06c69031..8eac0ed7 100644 --- a/nsd/conn.c +++ b/nsd/conn.c @@ -39,8 +39,8 @@ static int GetChan(Tcl_Interp *interp, const char *id, Tcl_Channel *chanPtr) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(3); -static int GetIndices(Tcl_Interp *interp, const Conn *connPtr, Tcl_Obj *CONST* objv, - int *offPtr, int *lenPtr) +static int GetIndices(Tcl_Interp *interp, const Conn *connPtr, + Tcl_Obj *CONST* objv, int *offPtr, int *lenPtr) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2); static Tcl_Channel MakeConnChannel(const NsInterp *itPtr, Ns_Conn *conn) @@ -235,7 +235,7 @@ size_t Ns_ConnContentSize(const Ns_Conn *conn) { NS_NONNULL_ASSERT(conn != NULL); - + return ((const Conn *)conn)->reqPtr->length; } @@ -259,9 +259,9 @@ const char * Ns_ConnContentFile(const Ns_Conn *conn) { const Sock *sockPtr; - + NS_NONNULL_ASSERT(conn != NULL); - + sockPtr = ((const Conn *)conn)->sockPtr; return sockPtr != NULL ? sockPtr->tfile : NULL; } @@ -286,9 +286,9 @@ int Ns_ConnContentFd(const Ns_Conn *conn) { const Sock *sockPtr; - + NS_NONNULL_ASSERT(conn != NULL); - + sockPtr = ((const Conn *)conn)->sockPtr; return sockPtr != NULL ? sockPtr->tfd : 0; @@ -315,7 +315,7 @@ const char * Ns_ConnServer(const Ns_Conn *conn) { NS_NONNULL_ASSERT(conn != NULL); - + return ((const Conn *)conn)->server; } @@ -340,7 +340,7 @@ int Ns_ConnResponseStatus(const Ns_Conn *conn) { NS_NONNULL_ASSERT(conn != NULL); - + return ((const Conn *)conn)->responseStatus; } @@ -349,7 +349,7 @@ void Ns_ConnSetResponseStatus(Ns_Conn *conn, int newStatus) { NS_NONNULL_ASSERT(conn != NULL); - + if (newStatus != 200) { ((Conn *)conn)->responseStatus = newStatus; } @@ -376,7 +376,7 @@ size_t Ns_ConnContentSent(const Ns_Conn *conn) { NS_NONNULL_ASSERT(conn != NULL); - + return ((const Conn *)conn)->nContentSent; } @@ -400,7 +400,7 @@ void Ns_ConnSetContentSent(Ns_Conn *conn, size_t length) { NS_NONNULL_ASSERT(conn != NULL); - + ((Conn *)conn)->nContentSent = length; } @@ -425,7 +425,7 @@ ssize_t Ns_ConnResponseLength(const Ns_Conn *conn) { NS_NONNULL_ASSERT(conn != NULL); - + return ((const Conn *)conn)->responseLength; } @@ -450,7 +450,7 @@ const char * Ns_ConnPeer(const Ns_Conn *conn) { NS_NONNULL_ASSERT(conn != NULL); - + return ((const Conn *)conn)->reqPtr->peer; } @@ -479,10 +479,10 @@ Ns_ConnSetPeer(const Ns_Conn *conn, const struct sockaddr *saPtr) NS_NONNULL_ASSERT(saPtr != NULL); connPtr = (Conn *)conn; - + connPtr->reqPtr->port = Ns_SockaddrGetPort(saPtr); (void)ns_inet_ntop(saPtr, connPtr->reqPtr->peer, NS_IPADDR_SIZE); - + return connPtr->reqPtr->peer; } @@ -507,7 +507,7 @@ unsigned short Ns_ConnPeerPort(const Ns_Conn *conn) { NS_NONNULL_ASSERT(conn != NULL); - + return ((const Conn *)conn)->reqPtr->port; } @@ -536,7 +536,7 @@ Ns_SetConnLocationProc(Ns_ConnLocationProc *proc, void *arg) NS_NONNULL_ASSERT(proc != NULL); NS_NONNULL_ASSERT(arg != NULL); - + if (servPtr == NULL) { Ns_Log(Error, "Ns_SetConnLocationProc: no initializing server"); status = NS_ERROR; @@ -574,7 +574,7 @@ Ns_SetLocationProc(const char *server, Ns_LocationProc *proc) NS_NONNULL_ASSERT(server != NULL); NS_NONNULL_ASSERT(proc != NULL); - + if (servPtr != NULL) { servPtr->vhost.locationProc = proc; } @@ -647,13 +647,13 @@ Ns_ConnLocationAppend(Ns_Conn *conn, Ns_DString *dest) NS_NONNULL_ASSERT(conn != NULL); NS_NONNULL_ASSERT(dest != NULL); - + connPtr = ((Conn *)conn); assert(connPtr->poolPtr != NULL); - + servPtr = connPtr->poolPtr->servPtr; assert(servPtr != NULL); - + if (servPtr->vhost.connLocationProc != NULL) { /* @@ -682,7 +682,7 @@ Ns_ConnLocationAppend(Ns_Conn *conn, Ns_DString *dest) * Construct a location string from the HTTP host header. */ if (Ns_StrIsHost(host)) { - /* + /* * We have here no port and no default port */ location = Ns_HttpLocationString(dest, connPtr->drvPtr->protocol, host, 0, 0); @@ -723,10 +723,10 @@ Ns_ConnHost(const Ns_Conn *conn) const Driver *drvPtr; NS_NONNULL_ASSERT(conn != NULL); - + drvPtr = ((const Conn *)conn)->drvPtr; assert(drvPtr != NULL); - + return drvPtr->address; } @@ -756,7 +756,7 @@ Ns_ConnPort(const Ns_Conn *conn) drvPtr = ((const Conn *)conn)->drvPtr; assert(drvPtr != NULL); - + return drvPtr->port; } @@ -781,11 +781,11 @@ NS_SOCKET Ns_ConnSock(const Ns_Conn *conn) { const Sock *sockPtr; - + NS_NONNULL_ASSERT(conn != NULL); - + sockPtr = ((const Conn *)conn)->sockPtr; - + return (sockPtr != NULL ? sockPtr->sock : NS_INVALID_SOCKET); } @@ -809,7 +809,7 @@ Ns_Sock * Ns_ConnSockPtr(const Ns_Conn *conn) { NS_NONNULL_ASSERT(conn != NULL); - + return (Ns_Sock *)(((const Conn *)conn))->sockPtr; } @@ -867,10 +867,10 @@ Ns_ConnDriverName(const Ns_Conn *conn) const Driver *drvPtr; NS_NONNULL_ASSERT(conn != NULL); - + drvPtr = ((const Conn *)conn)->drvPtr; assert(drvPtr != NULL); - + return drvPtr->moduleName; } @@ -952,7 +952,7 @@ Ns_ConnFilterTime(Ns_Conn *conn) * * Return for a given connection the time spans computed by * Ns_ConnTimeStats() - * + * * Results: * Four time structures (argument 2 to 5) * @@ -962,9 +962,9 @@ Ns_ConnFilterTime(Ns_Conn *conn) *---------------------------------------------------------------------- */ void -Ns_ConnTimeSpans(const Ns_Conn *conn, - Ns_Time *acceptTimeSpanPtr, Ns_Time *queueTimeSpanPtr, - Ns_Time *filterTimeSpanPtr, Ns_Time *runTimeSpanPtr) { +Ns_ConnTimeSpans(const Ns_Conn *conn, + Ns_Time *acceptTimeSpanPtr, Ns_Time *queueTimeSpanPtr, + Ns_Time *filterTimeSpanPtr, Ns_Time *runTimeSpanPtr) { const Conn *connPtr; NS_NONNULL_ASSERT(conn != NULL); @@ -989,8 +989,8 @@ Ns_ConnTimeSpans(const Ns_Conn *conn, * Compute for a given connection various time spans such as * acceptTimeSpan, queueTimeSpan, filterTimeSpan and * runTimeSpan as follows - * - * acceptTimeSpan = queueTime - acceptTime + * + * acceptTimeSpan = queueTime - acceptTime * queueTimeSpan = dequeueTime - queueTime * filterTimeSpan = filterDoneTime - dequeueTime * runTimeSpan = runDoneTime - filterDoneTime @@ -1011,7 +1011,7 @@ NsConnTimeStatsUpdate(Ns_Conn *conn) { NS_NONNULL_ASSERT(conn != NULL); connPtr = (Conn *) conn; - + Ns_GetTime(&connPtr->runDoneTime); (void)Ns_DiffTime(&connPtr->requestQueueTime, &connPtr->acceptTime, &connPtr->acceptTimeSpan); @@ -1030,7 +1030,7 @@ NsConnTimeStatsUpdate(Ns_Conn *conn) { * Record the time after running the connection main task and the end of * the processing of this task called traceTimeSpan. This value is * calculated as follows: - * + * * traceTimeSpan = now - runDoneTime * * In addition, this function updates the statistics and should @@ -1055,7 +1055,7 @@ NsConnTimeStatsFinalize(Ns_Conn *conn) { connPtr = (const Conn *) conn; poolPtr = connPtr->poolPtr; assert(poolPtr != NULL); - + Ns_GetTime(&now); (void)Ns_DiffTime(&now, &connPtr->runDoneTime, &diffTimeSpan); @@ -1092,7 +1092,7 @@ Ns_Time * Ns_ConnTimeout(Ns_Conn *conn) { NS_NONNULL_ASSERT(conn != NULL); - + return &((Conn *)conn)->timeout; } @@ -1117,7 +1117,7 @@ uintptr_t Ns_ConnId(const Ns_Conn *conn) { NS_NONNULL_ASSERT(conn != NULL); - + return ((const Conn *)conn)->id; } @@ -1125,7 +1125,7 @@ const char * NsConnIdStr(const Ns_Conn *conn) { NS_NONNULL_ASSERT(conn != NULL); - + return ((const Conn *)conn)->idstr; } @@ -1163,10 +1163,10 @@ Ns_ConnModifiedSince(const Ns_Conn *conn, time_t since) poolPtr = ((const Conn *)conn)->poolPtr; assert(poolPtr != NULL); assert(poolPtr->servPtr != NULL); - + if (poolPtr->servPtr->opts.modsince) { - char *hdr = Ns_SetIGet(conn->headers, "If-Modified-Since"); - + char *hdr = Ns_SetIGet(conn->headers, "If-Modified-Since"); + if (hdr != NULL && Ns_ParseHttpTime(hdr) >= since) { result = NS_FALSE; } @@ -1226,7 +1226,7 @@ Tcl_Encoding Ns_ConnGetEncoding(const Ns_Conn *conn) { NS_NONNULL_ASSERT(conn != NULL); - + return ((const Conn *) conn)->outputEncoding; } @@ -1234,7 +1234,7 @@ void Ns_ConnSetEncoding(Ns_Conn *conn, Tcl_Encoding encoding) { NS_NONNULL_ASSERT(conn != NULL); - + ((Conn *) conn)->outputEncoding = encoding; } @@ -1260,7 +1260,7 @@ Tcl_Encoding Ns_ConnGetUrlEncoding(const Ns_Conn *conn) { NS_NONNULL_ASSERT(conn != NULL); - + return ((const Conn *) conn)->urlEncoding; } @@ -1268,7 +1268,7 @@ void Ns_ConnSetUrlEncoding(Ns_Conn *conn, Tcl_Encoding encoding) { NS_NONNULL_ASSERT(conn != NULL); - + ((Conn *) conn)->urlEncoding = encoding; } @@ -1296,7 +1296,7 @@ int Ns_ConnGetCompression(const Ns_Conn *conn) { NS_NONNULL_ASSERT(conn != NULL); - + return ((const Conn *) conn)->requestCompress; } @@ -1335,7 +1335,7 @@ NsTclConnObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CO NsInterp *itPtr = clientData; Ns_Conn *conn = itPtr->conn; Conn *connPtr = (Conn *) conn; - const Ns_Request *request; + const Ns_Request *request = NULL; Tcl_Encoding encoding; Tcl_Channel chan; const Tcl_HashEntry *hPtr; @@ -1344,49 +1344,49 @@ NsTclConnObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CO const char *setName; static const char *const opts[] = { - "auth", "authpassword", "authuser", - "channel", "clientdata", "close", "compress", "content", - "contentfile", "contentlength", "contentsentlength", "copy", - "driver", - "encoding", - "fileheaders", "filelength", "fileoffset", "files", "flags", "form", - "headers", "host", - "id", "isconnected", - "keepalive", - "location", - "method", - "outputheaders", - "partialtimes", "peeraddr", "peerport", "pool", "port", "protocol", - "query", - "request", - "server", "sock", "start", "status", - "timeout", - "url", "urlc", "urlencoding", "urlv", - "version", - "zipaccepted", + "auth", "authpassword", "authuser", + "channel", "clientdata", "close", "compress", "content", + "contentfile", "contentlength", "contentsentlength", "copy", + "driver", + "encoding", + "fileheaders", "filelength", "fileoffset", "files", "flags", "form", + "headers", "host", + "id", "isconnected", + "keepalive", + "location", + "method", + "outputheaders", + "partialtimes", "peeraddr", "peerport", "pool", "port", "protocol", + "query", + "request", + "server", "sock", "start", "status", + "timeout", + "url", "urlc", "urlencoding", "urlv", + "version", + "zipaccepted", NULL }; enum ISubCmdIdx { - CAuthIdx, CAuthPasswordIdx, CAuthUserIdx, - CChannelIdx, CClientdataIdx, CCloseIdx, CCompressIdx, CContentIdx, - CContentFileIdx, CContentLengthIdx, CContentSentLenIdx, CCopyIdx, - CDriverIdx, - CEncodingIdx, - CFileHdrIdx, CFileLenIdx, CFileOffIdx, CFilesIdx, CFlagsIdx, CFormIdx, - CHeadersIdx, CHostIdx, - CIdIdx, CIsConnectedIdx, - CKeepAliveIdx, - CLocationIdx, - CMethodIdx, - COutputHeadersIdx, - CPartialTimesIdx, CPeerAddrIdx, CPeerPortIdx, CPoolIdx, CPortIdx, CProtocolIdx, - CQueryIdx, - CRequestIdx, - CServerIdx, CSockIdx, CStartIdx, CStatusIdx, - CTimeoutIdx, - CUrlIdx, CUrlcIdx, CUrlEncodingIdx, CUrlvIdx, - CVersionIdx, - CZipacceptedIdx + CAuthIdx, CAuthPasswordIdx, CAuthUserIdx, + CChannelIdx, CClientdataIdx, CCloseIdx, CCompressIdx, CContentIdx, + CContentFileIdx, CContentLengthIdx, CContentSentLenIdx, CCopyIdx, + CDriverIdx, + CEncodingIdx, + CFileHdrIdx, CFileLenIdx, CFileOffIdx, CFilesIdx, CFlagsIdx, CFormIdx, + CHeadersIdx, CHostIdx, + CIdIdx, CIsConnectedIdx, + CKeepAliveIdx, + CLocationIdx, + CMethodIdx, + COutputHeadersIdx, + CPartialTimesIdx, CPeerAddrIdx, CPeerPortIdx, CPoolIdx, CPortIdx, CProtocolIdx, + CQueryIdx, + CRequestIdx, + CServerIdx, CSockIdx, CStartIdx, CStatusIdx, + CTimeoutIdx, + CUrlIdx, CUrlcIdx, CUrlEncodingIdx, CUrlvIdx, + CVersionIdx, + CZipacceptedIdx }; if (unlikely(objc < 2)) { @@ -1395,30 +1395,45 @@ NsTclConnObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CO result = TCL_ERROR; } else if (unlikely(Tcl_GetIndexFromObj(interp, objv[1], opts, "option", 0, - &opt) != TCL_OK)) { + &opt) != TCL_OK)) { result = TCL_ERROR; } - - /* - * Only the "isconnected" option operates without a conn. - */ - if (likely(result == TCL_OK) - && unlikely(opt != (int)CIsConnectedIdx) - && unlikely(connPtr == NULL) - ) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("no current connection", -1)); - result = TCL_ERROR; + + if (likely(result == TCL_OK)) { + /* + * Only the "isconnected" option operates without a conn. + */ + if (unlikely(opt == (int)CIsConnectedIdx)) { + /* + * Handle "isconnected" subcommand here. + */ + Tcl_SetObjResult(interp, + Tcl_NewBooleanObj((connPtr != NULL) ? 1 : 0)); + } else if (unlikely(connPtr == NULL)) { + /* + * Other subcommands require connPtr + */ + Tcl_SetObjResult(interp, + Tcl_NewStringObj("no current connection", -1)); + result = TCL_ERROR; + } else { + /* + * We know, connPtr != NULL + */ + request = &connPtr->request; + } } if (result == TCL_ERROR) { return result; } - request = &connPtr->request; switch (opt) { - case CIsConnectedIdx: - Tcl_SetObjResult(interp, Tcl_NewBooleanObj((connPtr != NULL) ? 1 : 0)); + /* + * This case is handled above. We keep this entry to keep static + * checkers happy about case enumeration. + */ break; case CKeepAliveIdx: @@ -1432,13 +1447,13 @@ NsTclConnObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CO case CClientdataIdx: if (objc > 2) { - const char *value = Tcl_GetString(objv[2]); - if (connPtr->clientData != NULL) { - ns_free(connPtr->clientData); - } - connPtr->clientData = ns_strdup(value); - } - Tcl_SetObjResult(interp, Tcl_NewStringObj(connPtr->clientData, -1)); + const char *value = Tcl_GetString(objv[2]); + if (connPtr->clientData != NULL) { + ns_free(connPtr->clientData); + } + connPtr->clientData = ns_strdup(value); + } + Tcl_SetObjResult(interp, Tcl_NewStringObj(connPtr->clientData, -1)); break; case CCompressIdx: @@ -1464,7 +1479,7 @@ NsTclConnObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CO } else if (idx >= 0 && idx < request->urlc) { const char **elements; int length; - + Tcl_SplitList(NULL, request->urlv, &length, &elements); Tcl_SetObjResult(interp, Tcl_NewStringObj(elements[idx], -1)); Tcl_Free((char *) elements); @@ -1521,7 +1536,7 @@ NsTclConnObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CO result = TCL_ERROR; } else if ((connPtr->flags & NS_CONN_CLOSED) != 0u) { - /* + /* * In cases, the content is allocated via mmap, the content * is unmapped when the socket is closed. Accessing the * content will crash the server. Although we might not have @@ -1537,14 +1552,14 @@ NsTclConnObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CO Ns_TclPrintfResult(interp, "invalid offset and/or length specified"); result = TCL_ERROR; } - + requiredLength = length; if (result == TCL_OK && offset > 0 && ((size_t)offset > connPtr->reqPtr->length)) { Ns_TclPrintfResult(interp, "offset exceeds available content length"); result = TCL_ERROR; } - + if (result == TCL_OK && length == -1) { length = (int)connPtr->reqPtr->length - offset; @@ -1557,7 +1572,7 @@ NsTclConnObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CO if (result == TCL_OK) { size_t contentLength; const char *content; - + if (connPtr->reqPtr->length == 0u) { content = NULL; contentLength = 0u; @@ -1584,7 +1599,7 @@ NsTclConnObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CO if (!binary) { Tcl_DStringResult(interp, &encDs); } else { - Tcl_SetObjResult(interp, Tcl_NewByteArrayObj((uint8_t*)connPtr->reqPtr->content, + Tcl_SetObjResult(interp, Tcl_NewByteArrayObj((uint8_t*)connPtr->reqPtr->content, (int)connPtr->reqPtr->length)); } } else { @@ -1593,12 +1608,12 @@ NsTclConnObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CO */ if (!binary) { Tcl_Obj *contentObj = Tcl_NewStringObj(content, (int)contentLength); - + Tcl_SetObjResult(interp, Tcl_GetRange(contentObj, offset, offset+length-1)); Tcl_DStringFree(&encDs); Tcl_DecrRefCount(contentObj); } else { - Tcl_SetObjResult(interp, Tcl_NewByteArrayObj((const uint8_t*)content + offset, + Tcl_SetObjResult(interp, Tcl_NewByteArrayObj((const uint8_t*)content + offset, (int)length)); } } @@ -1701,10 +1716,10 @@ NsTclConnObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CO break; case CFormIdx: - if ((itPtr->nsconn.flags & CONN_TCLFORM) != 0u) { + if ((itPtr->nsconn.flags & CONN_TCLFORM) != 0u) { Tcl_SetResult(interp, itPtr->nsconn.form, TCL_STATIC); } else { - Ns_Set *form = Ns_ConnGetQuery(conn); + Ns_Set *form = Ns_ConnGetQuery(conn); if (form == NULL) { itPtr->nsconn.form[0] = '\0'; @@ -1753,7 +1768,7 @@ NsTclConnObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CO result = TCL_ERROR; } else { const FormFile *filePtr = Tcl_GetHashValue(hPtr); - + if (opt == (int)CFileOffIdx) { Tcl_SetObjResult(interp, (filePtr->offObj != NULL) ? filePtr->offObj : Tcl_NewObj()); } else if (opt == (int)CFileLenIdx) { @@ -1784,18 +1799,18 @@ NsTclConnObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CO break; case CRequestIdx: - Tcl_SetObjResult(interp, Tcl_NewStringObj(request->line, -1)); + Tcl_SetObjResult(interp, Tcl_NewStringObj(request->line, -1)); break; case CMethodIdx: - Tcl_SetObjResult(interp, Tcl_NewStringObj(request->method, -1)); + Tcl_SetObjResult(interp, Tcl_NewStringObj(request->method, -1)); break; - + case CPartialTimesIdx: { Ns_Time now, acceptTime, queueTime, filterTime, runTime; Tcl_DString ds, *dsPtr = &ds; - + Ns_GetTime(&now); Tcl_DStringInit(dsPtr); @@ -1826,7 +1841,7 @@ NsTclConnObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CO break; case CHostIdx: - Tcl_SetObjResult(interp, Tcl_NewStringObj(request->host, -1)); + Tcl_SetObjResult(interp, Tcl_NewStringObj(request->host, -1)); break; case CPortIdx: @@ -1834,11 +1849,11 @@ NsTclConnObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CO break; case CUrlIdx: - Tcl_SetObjResult(interp, Tcl_NewStringObj(request->url, request->url_len)); + Tcl_SetObjResult(interp, Tcl_NewStringObj(request->url, request->url_len)); break; case CQueryIdx: - Tcl_SetObjResult(interp, Tcl_NewStringObj(request->query, -1)); + Tcl_SetObjResult(interp, Tcl_NewStringObj(request->query, -1)); break; case CUrlcIdx: @@ -1852,7 +1867,7 @@ NsTclConnObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CO case CLocationIdx: { Tcl_DString ds; - + Ns_DStringInit(&ds); (void) Ns_ConnLocationAppend(conn, &ds); Tcl_DStringResult(interp, &ds); @@ -1864,7 +1879,7 @@ NsTclConnObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CO break; case CServerIdx: - Tcl_SetObjResult(interp, Tcl_NewStringObj(Ns_ConnServer(conn), -1)); + Tcl_SetObjResult(interp, Tcl_NewStringObj(Ns_ConnServer(conn), -1)); break; case CPoolIdx: @@ -1875,13 +1890,13 @@ NsTclConnObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CO if (objc < 2 || objc > 3) { Tcl_WrongNumArgs(interp, 2, objv, "?status?"); result = TCL_ERROR; - + } else if (objc == 3) { int status; - + if (Tcl_GetIntFromObj(interp, objv[2], &status) != TCL_OK) { result = TCL_ERROR; - + } else { Tcl_SetObjResult(interp,Tcl_NewIntObj(Ns_ConnResponseStatus(conn))); Ns_ConnSetResponseStatus(conn, status); @@ -1904,7 +1919,7 @@ NsTclConnObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CO break; case CFlagsIdx: - Tcl_SetObjResult(interp, Tcl_NewIntObj((int)connPtr->flags)); + Tcl_SetObjResult(interp, Tcl_NewIntObj((int)connPtr->flags)); break; case CStartIdx: @@ -1912,7 +1927,7 @@ NsTclConnObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CO break; case CCloseIdx: - (void) Ns_ConnClose(conn); + (void) Ns_ConnClose(conn); break; case CChannelIdx: @@ -1923,15 +1938,15 @@ NsTclConnObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CO Tcl_RegisterChannel(interp, chan); Tcl_SetObjResult(interp, Tcl_NewStringObj(Tcl_GetChannelName(chan),-1)); } - break; + break; case CContentSentLenIdx: if (objc == 2) { Tcl_SetObjResult(interp, Tcl_NewWideIntObj((Tcl_WideInt)connPtr->nContentSent)); - + } else if (objc == 3) { - Tcl_WideInt sent; - + Tcl_WideInt sent; + if (Tcl_GetWideIntFromObj(interp, objv[2], &sent) != TCL_OK) { result = TCL_ERROR; } else { @@ -1941,11 +1956,11 @@ NsTclConnObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CO Tcl_WrongNumArgs(interp, 2, objv, "?value?"); result = TCL_ERROR; } - break; + break; case CZipacceptedIdx: - Tcl_SetObjResult(interp, Tcl_NewBooleanObj((connPtr->flags & NS_CONN_ZIPACCEPTED) != 0u)); - break; + Tcl_SetObjResult(interp, Tcl_NewBooleanObj((connPtr->flags & NS_CONN_ZIPACCEPTED) != 0u)); + break; default: /* unexpected value */ @@ -1988,7 +2003,7 @@ NsTclLocationProcObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int o Ns_TclPrintfResult(interp, "no initializing server"); result = TCL_ERROR; } else { - Ns_TclCallback *cbPtr = Ns_TclNewCallback(interp, (Ns_Callback *)NsTclConnLocation, + Ns_TclCallback *cbPtr = Ns_TclNewCallback(interp, (Ns_Callback *)NsTclConnLocation, objv[1], objc - 2, objv + 2); if (Ns_SetConnLocationProc(NsTclConnLocation, cbPtr) != NS_OK) { result = TCL_ERROR; @@ -2040,17 +2055,17 @@ NsTclWriteContentObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl if (NsConnRequire(interp, NULL) != NS_OK || Ns_ParseObjv(opts, args, interp, 1, objc, objv) != NS_OK) { result = TCL_ERROR; - + } else if (GetChan(interp, chanName, &chan) != TCL_OK) { result = TCL_ERROR; } else if (Tcl_Flush(chan) != TCL_OK) { - Ns_TclPrintfResult(interp, "flush returned error: %s", strerror(Tcl_GetErrno())); - result = TCL_ERROR; + Ns_TclPrintfResult(interp, "flush returned error: %s", strerror(Tcl_GetErrno())); + result = TCL_ERROR; } else { const Request *reqPtr = ((Conn *)itPtr->conn)->reqPtr; - + if (toCopy > (int)reqPtr->avail || toCopy <= 0) { toCopy = (int)reqPtr->avail; } @@ -2088,7 +2103,7 @@ NsTclConnLocation(Ns_Conn *conn, Ns_DString *dest, const void *arg) char *result; if (Ns_TclEvalCallback(interp, cbPtr, dest, (char *)0) != TCL_OK) { - (void) Ns_TclLogErrorInfo(interp, "\n(context: location callback)"); + (void) Ns_TclLogErrorInfo(interp, "\n(context: location callback)"); result = NULL; } else { result = Ns_DStringValue(dest); @@ -2127,7 +2142,7 @@ GetChan(Tcl_Interp *interp, const char *id, Tcl_Channel *chanPtr) chan = Tcl_GetChannel(interp, id, &mode); if (chan == (Tcl_Channel) NULL) { result = TCL_ERROR; - + } else if ((mode & TCL_WRITABLE) == 0) { Ns_TclPrintfResult(interp, "channel \"%s\" wasn't opened for writing", id); result = TCL_ERROR; @@ -2264,7 +2279,7 @@ MakeConnChannel(const NsInterp *itPtr, Ns_Conn *conn) } } } - + return chan; } @@ -2304,7 +2319,7 @@ NsConnRequire(Tcl_Interp *interp, Ns_Conn **connPtr) } status = NS_OK; } - + return status; } diff --git a/nsd/connchan.c b/nsd/connchan.c index 8f3af018..90571601 100644 --- a/nsd/connchan.c +++ b/nsd/connchan.c @@ -57,6 +57,16 @@ typedef struct Callback { char script[1]; } Callback; +/* + * The following structure is used for a socket listen callback. + */ + +typedef struct ListenCallback { + const char *server; + const char *driverName; + char script[1]; +} ListenCallback; + /* * Local functions defined in this file @@ -86,6 +96,8 @@ static ssize_t DriverRecv(Sock *sockPtr, struct iovec *bufs, int nbufs, Ns_Time static ssize_t DriverSend(Tcl_Interp *interp, const NsConnChan *connChanPtr, struct iovec *bufs, int nbufs, unsigned int flags, const Ns_Time *timeoutPtr) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(6); +static bool SockListenCallback(NS_SOCKET sock, void *arg, unsigned int UNUSED(why)); + static Ns_SockProc NsTclConnChanProc; static Tcl_ObjCmdProc ConnChanCallbackObjCmd; @@ -93,6 +105,7 @@ static Tcl_ObjCmdProc ConnChanCloseObjCmd; static Tcl_ObjCmdProc ConnChanDetachObjCmd; static Tcl_ObjCmdProc ConnChanExistsObjCmd; static Tcl_ObjCmdProc ConnChanListObjCmd; +static Tcl_ObjCmdProc ConnChanListenObjCmd; static Tcl_ObjCmdProc ConnChanOpenObjCmd; static Tcl_ObjCmdProc ConnChanReadObjCmd; static Tcl_ObjCmdProc ConnChanWriteObjCmd; @@ -342,7 +355,7 @@ ConnChanGet(Tcl_Interp *interp, NsServer *servPtr, const char *name) { Ns_MutexUnlock(&servPtr->connchans.lock); if (connChanPtr == NULL && interp != NULL) { - Ns_TclPrintfResult(interp, "connchan \"%s\" does not exist", name); + Ns_TclPrintfResult(interp, "channel \"%s\" does not exist", name); } return connChanPtr; @@ -568,7 +581,7 @@ DriverRecv(Sock *sockPtr, struct iovec *bufs, int nbufs, Ns_Time *timeoutPtr) if (likely(sockPtr->drvPtr->recvProc != NULL)) { result = (*sockPtr->drvPtr->recvProc)((Ns_Sock *) sockPtr, bufs, nbufs, timeoutPtr, 0u); } else { - Ns_Log(Warning, "connchan: no recvProc registered for driver %s", sockPtr->drvPtr->moduleName); + Ns_Log(Warning, "ns_connchan: no recvProc registered for driver %s", sockPtr->drvPtr->moduleName); result = -1; } @@ -636,7 +649,7 @@ DriverSend(Tcl_Interp *interp, const NsConnChan *connChanPtr, timeoutPtr, flags); } else { haveTimeout = NS_TRUE; - Ns_TclPrintfResult(interp, "connchan %s: timeout on send operation (%ld:%ld)", + Ns_TclPrintfResult(interp, "channel %s: timeout on send operation (%ld:%ld)", connChanPtr->channelName, timeoutPtr->sec, timeoutPtr->usec); result = -1; } @@ -664,7 +677,7 @@ DriverSend(Tcl_Interp *interp, const NsConnChan *connChanPtr, /* * Timeout is handled above. */ - Ns_TclPrintfResult(interp, "connchan %s: send operation failed: %s", + Ns_TclPrintfResult(interp, "channel %s: send operation failed: %s", connChanPtr->channelName, strerror(errno)); } @@ -676,7 +689,7 @@ DriverSend(Tcl_Interp *interp, const NsConnChan *connChanPtr, } else { - Ns_TclPrintfResult(interp, "connchan %s: no sendProc registered for driver %s", + Ns_TclPrintfResult(interp, "channel %s: no sendProc registered for driver %s", connChanPtr->channelName, sockPtr->drvPtr->moduleName); result = -1; } @@ -870,6 +883,201 @@ ConnChanOpenObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj return result; } +/* + *---------------------------------------------------------------------- + * + * ConnChanListenObjCmd -- + * + * Implements the "ns_connchan listen" command. + * + * Results: + * Tcl result. + * + * Side effects: + * Depends on subcommand. + * + *---------------------------------------------------------------------- + */ + +static int +ConnChanListenObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) +{ + int result = TCL_OK; + unsigned short port; + char *driverName = NULL, *serverName = NULL, *addr, *script; + bool doBind = NS_FALSE; + Ns_ObjvSpec lopts[] = { + {"-driver", Ns_ObjvString, &driverName, NULL}, + {"-server", Ns_ObjvString, &serverName, NULL}, + {"-bind", Ns_ObjvBool, &doBind, INT2PTR(NS_TRUE)}, + {NULL, NULL, NULL, NULL} + }; + Ns_ObjvSpec largs[] = { + {"address", Ns_ObjvString, &addr, NULL}, + {"port", Ns_ObjvUShort, &port, NULL}, + {"script", Ns_ObjvString, &script, NULL}, + {NULL, NULL, NULL, NULL} + }; + + if (Ns_ParseObjv(lopts, largs, interp, 2, objc, objv) != NS_OK) { + result = TCL_ERROR; + + } else { + const NsInterp *itPtr = clientData; + ListenCallback *lcbPtr; + size_t scriptLength; + NS_SOCKET sock; + + if (STREQ(addr, "*")) { + addr = NULL; + } + scriptLength = strlen(script); + lcbPtr = ns_malloc(sizeof(ListenCallback) + scriptLength); + if (serverName == NULL) { + serverName = (itPtr->servPtr != NULL ? (char *)itPtr->servPtr->server : NULL); + } + if (serverName == NULL) { + serverName = (char *)nsconf.defaultServer; + } + + lcbPtr->server = serverName; + memcpy(lcbPtr->script, script, scriptLength + 1u); + lcbPtr->driverName = ns_strcopy(driverName); + + sock = Ns_SockListenCallback(addr, port, SockListenCallback, doBind, lcbPtr); + Ns_Log(Notice, "ns_connchan listen calls Ns_SockListenCallback, returning %d", sock); + if (sock == NS_INVALID_SOCKET) { + Ns_TclPrintfResult(interp, "could not register callback"); + ns_free(lcbPtr); + result = TCL_ERROR; + } else { + struct NS_SOCKADDR_STORAGE sa; + socklen_t len = (socklen_t)sizeof(sa); + Sock *sockPtr = NULL; + + result = NSDriverSockNew(interp, sock, "http", lcbPtr->driverName, "CONNECT", &sockPtr); + if (result == TCL_OK && sockPtr->servPtr != NULL) { + Ns_Time now; + NsConnChan *connChanPtr; + int retVal; + + Ns_GetTime(&now); + connChanPtr = ConnChanCreate(sockPtr->servPtr, + sockPtr, + &now, + sockPtr->reqPtr->peer, + NS_TRUE /* binary, fixed for the time being */, + NULL); + retVal = getsockname(sock, (struct sockaddr *) &sa, &len); + if (retVal == -1) { + Ns_TclPrintfResult(interp, "can't obtain socket info %s", ns_sockstrerror(ns_sockerrno)); + ConnChanFree(connChanPtr); + result = TCL_ERROR; + } else { + Tcl_Obj *listObj = Tcl_NewListObj(0, NULL); + char ipString[NS_IPADDR_SIZE]; + + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("channel", 7)); + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(connChanPtr->channelName, -1)); + + port = Ns_SockaddrGetPort((struct sockaddr *) &sa); + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("port", 4)); + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewIntObj((int)port)); + + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("sock", 4)); + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewIntObj((int)sock)); + + ns_inet_ntop((struct sockaddr *) &sa, ipString, sizeof(ipString)); + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("address", 7)); + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(ipString, -1)); + + Tcl_SetObjResult(interp, listObj); + } + } + } + } + Ns_Log(Notice, "ns_connchan listen returns %d", result); + return result; +} + +/* + *---------------------------------------------------------------------- + * + * SockListenCallback -- + * + * This is the C wrapper callback that is registered from + * ListenCallback. + * + * Results: + * NS_TRUE or NS_FALSE on error + * + * Side effects: + * Will run Tcl script. + * + *---------------------------------------------------------------------- + */ + +static bool +SockListenCallback(NS_SOCKET sock, void *arg, unsigned int UNUSED(why)) +{ + const ListenCallback *lcbPtr; + Tcl_Interp *interp; + int result; + Sock *sockPtr = NULL; + Ns_Time now; + Tcl_Obj *listObj = Tcl_NewListObj(0, NULL); + NsConnChan *connChanPtr = NULL; + + assert(arg != NULL); + + lcbPtr = arg; + interp = Ns_TclAllocateInterp(lcbPtr->server); + + result = NSDriverSockNew(interp, sock, "http", lcbPtr->driverName, "CONNECTED", &sockPtr); + + if (result == TCL_OK) { + Ns_GetTime(&now); + connChanPtr = ConnChanCreate(sockPtr->servPtr, + sockPtr, + &now, + sockPtr->reqPtr->peer, + NS_TRUE /* binary, fixed for the time being */, + NULL); + Ns_Log(Notice, "SockListenCallback new connChan %s sock %d", connChanPtr->channelName, sock); + } + + if (connChanPtr != NULL) { + Tcl_DString script; + + Tcl_DStringInit(&script); + Tcl_DStringAppend(&script, lcbPtr->script, -1); + Tcl_DStringAppendElement(&script, connChanPtr->channelName); + result = Tcl_EvalEx(interp, script.string, script.length, 0); + Tcl_DStringFree(&script); + if (result != TCL_OK) { + (void) Ns_TclLogErrorInfo(interp, "\n(context: connchan proc)"); + } else { + Tcl_Obj *objPtr = Tcl_GetObjResult(interp); + int ok = 1; + + /* + * The Tcl callback can signal with the result "0", + * that the connection channel should be closed + * automatically. + */ + result = Tcl_GetBooleanFromObj(interp, objPtr, &ok); + if ((result == TCL_OK) && (ok == 0)) { + result = TCL_ERROR; + } + } + } + + Ns_TclDeAllocateInterp(interp); + Tcl_DecrRefCount(listObj); + + return (result == TCL_OK); +} + /* *---------------------------------------------------------------------- * @@ -1298,12 +1506,12 @@ NsTclConnChanObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj {"detach", ConnChanDetachObjCmd}, {"exists", ConnChanExistsObjCmd}, {"list", ConnChanListObjCmd}, + {"listen", ConnChanListenObjCmd}, {"open", ConnChanOpenObjCmd}, {"read", ConnChanReadObjCmd}, {"write", ConnChanWriteObjCmd}, {NULL, NULL} }; - return Ns_SubcmdObjv(subcmds, clientData, interp, objc, objv); } diff --git a/nsd/cookies.c b/nsd/cookies.c index d0f235da..197bb4ee 100644 --- a/nsd/cookies.c +++ b/nsd/cookies.c @@ -11,7 +11,7 @@ * * The Original Code is AOLserver Code and related documentation * distributed by AOL. - * + * * The Initial Developer of the Original Code is America Online, * Inc. Portions created by AOL are Copyright (C) 1999 America Online, * Inc. All Rights Reserved. @@ -40,20 +40,27 @@ * Local functions defined in this file. */ -static int SearchFirstCookie(Ns_DString *dest, const Ns_Set *hdrs, const char *setName, const char *name) +static int GetFirstNamedCookie(Ns_DString *dest, const Ns_Set *hdrs, + const char *setName, const char *name) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(3) NS_GNUC_NONNULL(4); -static bool DeleteNamedCookies(Ns_Set *hdrs, const char *setName, const char *name) +static int GetAllNamedCookies(Ns_DString *dest, const Ns_Set *hdrs, + const char *setName, const char *name) + NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(3) NS_GNUC_NONNULL(4); + +static bool DeleteNamedCookies(Ns_Set *hdrs, const char *setName, + const char *name) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(3); -typedef char* (CookieParser)(Ns_DString *dest, char *chars, const char *name, size_t nameLen) +typedef char* (CookieParser)(Ns_DString *dest, char *chars, const char *name, + size_t nameLen, char **nextPtr) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(3); static CookieParser GetFromCookieHeader; static CookieParser GetFromSetCookieHeader; -static void CopyCookieValue(Tcl_DString *dest, char *valueStart) +static char *CopyCookieValue(Tcl_DString *dest, char *valueStart) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2); @@ -64,7 +71,7 @@ static void CopyCookieValue(Tcl_DString *dest, char *valueStart) * * Copy the CookieValue into the provided Tcl_DString * - * Results: + * Results: * None * * Side effects: @@ -73,16 +80,19 @@ static void CopyCookieValue(Tcl_DString *dest, char *valueStart) *---------------------------------------------------------------------- */ -static void +static char * CopyCookieValue(Tcl_DString *dest, char *valueStart) { char save, *q; NS_NONNULL_ASSERT(dest != NULL); NS_NONNULL_ASSERT(valueStart != NULL); - + if (*valueStart == '"') { - ++valueStart; /* advance past optional quote mark */ + /* + * Advance past optional quote. + */ + ++valueStart; } q = valueStart; while (*q != '"' && *q != ';' && *q != '\0') { @@ -92,6 +102,15 @@ CopyCookieValue(Tcl_DString *dest, char *valueStart) *q = '\0'; Ns_CookieDecode(dest, valueStart, NULL); *q = save; + + /* + * Advance past delimiter. + */ + while (*q == '"' || *q == ';') { + q++; + } + + return q; } @@ -108,7 +127,7 @@ CopyCookieValue(Tcl_DString *dest, char *valueStart) * so we have to iterate over the cookie/value pairs separated with * semicolons. * - * Results: + * Results: * On success a non-null value pointing the the begin of the found * cookie such we can iterate to search for more cookies with the same * name @@ -121,25 +140,26 @@ CopyCookieValue(Tcl_DString *dest, char *valueStart) */ static char * -GetFromCookieHeader(Ns_DString *dest, char *chars, const char *name, size_t nameLen) +GetFromCookieHeader(Ns_DString *dest, char *chars, const char *name, + size_t nameLen, char **nextPtr) { - char *cookieStart = NULL, *p = chars; + char *cookieStart = NULL, *toParse = chars; NS_NONNULL_ASSERT(chars != NULL); NS_NONNULL_ASSERT(name != NULL); - - for ( ; likely(*p != '\0'); ) { + + for ( ; likely(*toParse != '\0'); ) { /* * Skip optional white space. */ - for (; (CHARTYPE(space, *p) != 0); p++) { + for (; (CHARTYPE(space, *toParse) != 0); toParse++) { } - if (*p == '\0') { + if (*toParse == '\0') { break; } - - if (strncmp(p, name, nameLen) == 0) { - char *q = p + nameLen; + + if (strncmp(toParse, name, nameLen) == 0) { + char *q = toParse + nameLen; /* * Name starts correctly @@ -148,28 +168,33 @@ GetFromCookieHeader(Ns_DString *dest, char *chars, const char *name, size_t name /* * Full match, we found the cookie */ - cookieStart = p; + cookieStart = toParse; + q++; /* advance past equals sign */ if (dest != NULL) { - q++; /* advance past equals sign */ - CopyCookieValue(dest, q); + q = CopyCookieValue(dest, q); } + toParse = q; break; } } - /* + /* * Look for the next semicolon */ - for (; (*p != '\0') && (*p != ';'); p++) { + for (; (*toParse != '\0') && (*toParse != ';'); toParse++) { ; } - /* - * We found a semicolon and skip it; - */ - if (*p == ';') { - p++; + if (*toParse == ';') { + /* + * We found a semicolon and skip it; + */ + toParse++; } } + if (nextPtr != NULL) { + *nextPtr = toParse; + } + return cookieStart; } @@ -179,15 +204,15 @@ GetFromCookieHeader(Ns_DString *dest, char *chars, const char *name, size_t name * * GetFromSetCookieHeader -- * - * Get a cookie from the set-cookie header. The set-cookie header field has a - * content of the form: + * Get a cookie from the set-cookie header. The set-cookie header field + * has a content of the form: * * cookie1="new-value"; Expires=Fri, 01-Jan-2035 01:00:00 GMT; Path=/; HttpOnly * * In order to get the cookie-value, the entry has to start with a * name/value pair. * - * Results: + * Results: * On success a non-null value pointing the the begin of the found * cookie * @@ -199,8 +224,9 @@ GetFromCookieHeader(Ns_DString *dest, char *chars, const char *name, size_t name */ static char * -GetFromSetCookieHeader(Ns_DString *dest, char *chars, const char *name, size_t nameLen) { - char *cookieStart = NULL, *p = chars; +GetFromSetCookieHeader(Ns_DString *dest, char *chars, const char *name, + size_t nameLen, char **nextPtr) { + char *cookieStart = NULL, *toParse = chars; NS_NONNULL_ASSERT(chars != NULL); NS_NONNULL_ASSERT(name != NULL); @@ -208,27 +234,31 @@ GetFromSetCookieHeader(Ns_DString *dest, char *chars, const char *name, size_t n /* * Skip white space (should not be needed). */ - for (; (CHARTYPE(space, *p) != 0); p++) { + for (; (CHARTYPE(space, *toParse) != 0); toParse++) { ; } - if (strncmp(p, name, nameLen) == 0) { - char *q = p + nameLen; - + if (strncmp(toParse, name, nameLen) == 0) { + char *q = toParse + nameLen; + /* * Name starts correctly */ - + if (*q == '=') { /* * Full match, we found the cookie */ - cookieStart = p; + cookieStart = toParse; + q++; /* advance past equals sign */ if (dest != NULL) { - q++; /* advance past equals sign */ - CopyCookieValue(dest, q); + q = CopyCookieValue(dest, q); } + toParse = q; } } + if (nextPtr != NULL) { + *nextPtr = toParse; + } return cookieStart; } @@ -237,9 +267,9 @@ GetFromSetCookieHeader(Ns_DString *dest, char *chars, const char *name, size_t n /* *---------------------------------------------------------------------- * - * SearchFirstCookie -- + * GetFirstNamedCookie -- * - * Search for a coockie with the given name in the given set and + * Search for a cookie with the given name in the given set and * return the first hit. * * Results: @@ -252,8 +282,9 @@ GetFromSetCookieHeader(Ns_DString *dest, char *chars, const char *name, size_t n *---------------------------------------------------------------------- */ -static int -SearchFirstCookie(Ns_DString *dest, const Ns_Set *hdrs, const char *setName, const char *name) +static int +GetFirstNamedCookie(Ns_DString *dest, const Ns_Set *hdrs, const char *setName, + const char *name) { int index = -1; size_t nameLen, i; @@ -265,14 +296,17 @@ SearchFirstCookie(Ns_DString *dest, const Ns_Set *hdrs, const char *setName, con nameLen = strlen(name); - cookieParser = (*setName == 'c') ? GetFromCookieHeader : GetFromSetCookieHeader; - + cookieParser = (*setName == 'c') + ? GetFromCookieHeader + : GetFromSetCookieHeader; + for (i = 0u; i < hdrs->size; ++i) { if (strcasecmp(hdrs->fields[i].name, setName) == 0) { /* * We have the right header. */ - if ((*cookieParser)(dest, hdrs->fields[i].value, name, nameLen) != NULL) { + if ((*cookieParser)(dest, hdrs->fields[i].value, name, + nameLen, NULL) != NULL) { /* * We found the result. */ @@ -285,6 +319,68 @@ SearchFirstCookie(Ns_DString *dest, const Ns_Set *hdrs, const char *setName, con return index; } +/* + *---------------------------------------------------------------------- + * + * GetAllNamedCookies -- + * + * Search for a cookie with the given name in the given set and + * return all hits. + * + * Results: + * Number of cookies with the given name + * + * Side effects: + * Update the first argument with a list of cookie values + * + *---------------------------------------------------------------------- + */ +static int +GetAllNamedCookies(Ns_DString *dest, const Ns_Set *hdrs, const char *setName, + const char *name) +{ + int count = 0; + size_t nameLen, i; + CookieParser *cookieParser; + + NS_NONNULL_ASSERT(dest != NULL); + NS_NONNULL_ASSERT(hdrs != NULL); + NS_NONNULL_ASSERT(setName != NULL); + NS_NONNULL_ASSERT(name != NULL); + + nameLen = strlen(name); + cookieParser = (*setName == 'c') + ? GetFromCookieHeader + : GetFromSetCookieHeader; + + for (i = 0u; i < hdrs->size; i++) { + if (strcasecmp(hdrs->fields[i].name, setName) == 0) { + char *toParse; + + /* + * We have the right header, parse the string; + */ + for (toParse = hdrs->fields[i].value; *toParse != '\0'; ) { + Ns_DString cookie; + + Ns_DStringInit(&cookie); + if ((*cookieParser)(&cookie, toParse, name, nameLen, + &toParse) != NULL) { + /* + * We found the named cookie; + */ + count ++; + Tcl_DStringAppendElement(dest, cookie.string); + } + Ns_DStringFree(&cookie); + } + break; + } + } + + return count; +} + /* *---------------------------------------------------------------------- @@ -312,13 +408,13 @@ DeleteNamedCookies(Ns_Set *hdrs, const char *setName, const char *name) NS_NONNULL_ASSERT(name != NULL); for (;;) { - int idx = SearchFirstCookie(NULL, hdrs, setName, name); - if (idx != -1) { - Ns_SetDelete(hdrs, idx); - success = NS_TRUE; - } else { - break; - } + int idx = GetFirstNamedCookie(NULL, hdrs, setName, name); + if (idx != -1) { + Ns_SetDelete(hdrs, idx); + success = NS_TRUE; + } else { + break; + } } return success; } @@ -342,16 +438,18 @@ DeleteNamedCookies(Ns_Set *hdrs, const char *setName, const char *name) */ void -Ns_ConnSetCookieEx(const Ns_Conn *conn, const char *name, const char *value, time_t maxage, - const char *domain, const char *path, unsigned int flags) +Ns_ConnSetCookieEx(const Ns_Conn *conn, const char *name, const char *value, + time_t maxage, const char *domain, const char *path, + unsigned int flags) { Ns_DString cookie; NS_NONNULL_ASSERT(conn != NULL); NS_NONNULL_ASSERT(name != NULL); - + if ((flags & NS_COOKIE_REPLACE) != 0u) { - (void)DeleteNamedCookies(Ns_ConnOutputHeaders(conn), "set-cookie", name); + (void)DeleteNamedCookies(Ns_ConnOutputHeaders(conn), "set-cookie", + name); } Ns_DStringInit(&cookie); @@ -367,9 +465,9 @@ Ns_ConnSetCookieEx(const Ns_Conn *conn, const char *name, const char *value, tim } else if (maxage > 0) { Ns_DStringPrintf(&cookie, "; Max-Age=%ld", maxage); } else { - /* - * maxage == 0, don't specify any expiry - */ + /* + * maxage == 0, don't specify any expiry + */ } /* ignore empty domain, since IE rejects it */ if (domain != NULL && *domain != '\0') { @@ -393,16 +491,18 @@ Ns_ConnSetCookieEx(const Ns_Conn *conn, const char *name, const char *value, tim } void -Ns_ConnSetCookie(const Ns_Conn *conn, const char *name, const char *value, time_t maxage) +Ns_ConnSetCookie(const Ns_Conn *conn, const char *name, const char *value, + time_t maxage) { NS_NONNULL_ASSERT(conn != NULL); NS_NONNULL_ASSERT(name != NULL); - + Ns_ConnSetCookieEx(conn, name, value, maxage, NULL, NULL, 0u); } void -Ns_ConnSetSecureCookie(const Ns_Conn *conn, const char *name, const char *value, time_t maxage) +Ns_ConnSetSecureCookie(const Ns_Conn *conn, const char *name, const char *value, + time_t maxage) { NS_NONNULL_ASSERT(conn != NULL); NS_NONNULL_ASSERT(name != NULL); @@ -428,21 +528,25 @@ Ns_ConnSetSecureCookie(const Ns_Conn *conn, const char *name, const char *value, */ void -Ns_ConnDeleteCookie(const Ns_Conn *conn, const char *name, const char *domain, const char *path) +Ns_ConnDeleteCookie(const Ns_Conn *conn, const char *name, const char *domain, + const char *path) { NS_NONNULL_ASSERT(conn != NULL); NS_NONNULL_ASSERT(name != NULL); - - Ns_ConnSetCookieEx(conn, name, NULL, (time_t)0, domain, path, NS_COOKIE_EXPIRENOW); + + Ns_ConnSetCookieEx(conn, name, NULL, (time_t)0, domain, path, + NS_COOKIE_EXPIRENOW); } void -Ns_ConnDeleteSecureCookie(const Ns_Conn *conn, const char *name, const char *domain, const char *path) +Ns_ConnDeleteSecureCookie(const Ns_Conn *conn, const char *name, + const char *domain, const char *path) { NS_NONNULL_ASSERT(conn != NULL); NS_NONNULL_ASSERT(name != NULL); - - Ns_ConnSetCookieEx(conn, name, NULL, (time_t)0, domain, path, NS_COOKIE_EXPIRENOW|NS_COOKIE_SECURE); + + Ns_ConnSetCookieEx(conn, name, NULL, (time_t)0, domain, path, + NS_COOKIE_EXPIRENOW|NS_COOKIE_SECURE); } @@ -471,9 +575,9 @@ Ns_ConnGetCookie(Ns_DString *dest, const Ns_Conn *conn, const char *name) NS_NONNULL_ASSERT(dest != NULL); NS_NONNULL_ASSERT(conn != NULL); NS_NONNULL_ASSERT(name != NULL); - - idx = SearchFirstCookie(dest, Ns_ConnHeaders(conn), "cookie", name); - + + idx = GetFirstNamedCookie(dest, Ns_ConnHeaders(conn), "cookie", name); + return idx != -1 ? Ns_DStringValue(dest) : NULL; } @@ -496,7 +600,8 @@ Ns_ConnGetCookie(Ns_DString *dest, const Ns_Conn *conn, const char *name) */ int -NsTclSetCookieObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) +NsTclSetCookieObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, + int objc, Tcl_Obj *CONST* objv) { Ns_Conn *conn; char *name, *data, *domain = NULL, *path = NULL; @@ -518,7 +623,7 @@ NsTclSetCookieObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc {"data", Ns_ObjvString, &data, NULL}, {NULL, NULL, NULL, NULL} }; - + if (Ns_ParseObjv(opts, args, interp, 1, objc, objv) != NS_OK || NsConnRequire(interp, &conn) != NS_OK) { result = TCL_ERROR; @@ -547,7 +652,10 @@ NsTclSetCookieObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc */ if (expiresPtr != NULL) { - const Ns_Time *nowPtr = Ns_ConnStartTime(conn); /* Approximately now... */ + /* + * The start time is close enough to "now" + */ + const Ns_Time *nowPtr = Ns_ConnStartTime(conn); if (expiresPtr->sec < 0) { maxage = TIME_T_MAX; } else if (expiresPtr->sec > nowPtr->sec) { @@ -585,15 +693,17 @@ NsTclSetCookieObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc */ int -NsTclGetCookieObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) +NsTclGetCookieObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, + int objc, Tcl_Obj *CONST* objv) { Ns_Conn *conn; char *nameString; Tcl_Obj *defaultObj = NULL; int status = TCL_OK; - int withSetCookies = (int)NS_FALSE; + int withSetCookies = (int)NS_FALSE, withAll = (int)NS_FALSE; Ns_ObjvSpec opts[] = { + {"-all", Ns_ObjvBool, &withAll, NULL}, {"-include_set_cookies", Ns_ObjvBool, &withSetCookies, NULL}, {"--", Ns_ObjvBreak, NULL, NULL}, {NULL, NULL, NULL, NULL} @@ -608,30 +718,42 @@ NsTclGetCookieObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc || NsConnRequire(interp, &conn) != NS_OK) { status = TCL_ERROR; + } else if (withSetCookies == (int)NS_TRUE && withAll == (int)NS_TRUE) { + Ns_TclPrintfResult(interp, "%s", "invalid combination of flags -include_set_cookies and -all"); + status = TCL_ERROR; + } else { Ns_DString ds; int idx = -1; Ns_DStringInit(&ds); - if (withSetCookies == (int)NS_TRUE) { - idx = SearchFirstCookie(&ds, Ns_ConnOutputHeaders(conn), "set-cookie", nameString); - } - if (idx == -1) { - idx = SearchFirstCookie(&ds, Ns_ConnHeaders(conn), "cookie", nameString); + if (withAll == (int)NS_TRUE) { + idx = GetAllNamedCookies(&ds, Ns_ConnHeaders(conn), + "cookie", nameString); + + } else { + if (withSetCookies == (int)NS_TRUE) { + idx = GetFirstNamedCookie(&ds, Ns_ConnOutputHeaders(conn), + "set-cookie", nameString); + } + if (idx == -1) { + idx = GetFirstNamedCookie(&ds, Ns_ConnHeaders(conn), + "cookie", nameString); + } } - + if (idx != -1) { Tcl_DStringResult(interp, &ds); } else if (defaultObj != NULL) { Tcl_SetObjResult(interp, defaultObj); } else { - Tcl_SetObjResult(interp, Tcl_NewStringObj("no matching cookie", -1)); + Tcl_SetObjResult(interp, Tcl_NewStringObj("no such cookie", -1)); status = TCL_ERROR; } Ns_DStringFree(&ds); } - + return status; } @@ -653,7 +775,8 @@ NsTclGetCookieObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc */ int -NsTclDeleteCookieObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) +NsTclDeleteCookieObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, + int objc, Tcl_Obj *CONST* objv) { Ns_Conn *conn; char *name, *domain = NULL, *path = NULL; @@ -685,7 +808,8 @@ NsTclDeleteCookieObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int o flags |= NS_COOKIE_SECURE; } - Ns_ConnSetCookieEx(conn, name, NULL, (time_t)0, domain, path, NS_COOKIE_EXPIRENOW|flags); + Ns_ConnSetCookieEx(conn, name, NULL, (time_t)0, domain, path, + NS_COOKIE_EXPIRENOW|flags); result = TCL_OK; } diff --git a/nsd/dns.c b/nsd/dns.c index 85d5d6ac..e0a58ad6 100644 --- a/nsd/dns.c +++ b/nsd/dns.c @@ -222,7 +222,7 @@ DnsGet(GetProc *getProc, Ns_DString *dsPtr, Ns_Cache *cache, const char *key, bo Ns_CacheSetValueExpires(entry, ns_strdup(ds.string), (size_t)ds.length, &endTime, (int)(diffTime.sec * 1000000 + diffTime.usec), - 0u); + 0u, 0u); } Ns_CacheBroadcast(cache); } else { diff --git a/nsd/driver.c b/nsd/driver.c index 543e8252..9ae3eeb7 100644 --- a/nsd/driver.c +++ b/nsd/driver.c @@ -132,10 +132,11 @@ static void DriverClose(Sock *sockPtr) static Ns_ReturnCode DriverInit(const char *server, const char *moduleName, const char *threadName, const Ns_DriverInitData *init, NsServer *servPtr, const char *path, const char *bindaddr, - const char *defserver, const char *address, const char *host, - int driverCount) + const char *defserver, const char *address, const char *host) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(3) NS_GNUC_NONNULL(4) NS_GNUC_NONNULL(6) NS_GNUC_NONNULL(7) NS_GNUC_NONNULL(9) NS_GNUC_NONNULL(10); +static bool DriverModuleInitialized(const char *module) + NS_GNUC_NONNULL(1); static void SockSetServer(Sock *sockPtr) NS_GNUC_NONNULL(1); @@ -204,6 +205,11 @@ static void RequestFree(Sock *sockPtr) static void LogBuffer(Ns_LogSeverity severity, const char *msg, const char *buffer, size_t len) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(3); +static void ServerMapEntryAdd(Tcl_DString *dsPtr, const char *host, const char *moduleName, + NsServer *servPtr, Driver *drvPtr, const ServerMap **defMapPtrPtr) + NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(3) NS_GNUC_NONNULL(4) NS_GNUC_NONNULL(5); + + /* * Static variables defined in this file. */ @@ -262,6 +268,42 @@ NsInitDrivers(void) Ns_MutexSetName2(&writerlock, "ns:writer", "stream"); } + +/* + *---------------------------------------------------------------------- + * + * DriverModuleInitialized -- + * + * Check if a driver with the specified name is already initialized. + * + * Results: + * Boolean + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ +static bool +DriverModuleInitialized(const char *module) +{ + Driver *drvPtr; + bool found = NS_FALSE; + + NS_NONNULL_ASSERT(module != NULL); + + for (drvPtr = firstDrvPtr; drvPtr != NULL; drvPtr = drvPtr->nextPtr) { + + if (strcmp(drvPtr->moduleName, module) == 0) { + found = NS_TRUE; + Ns_Log(Notice, "Driver %s is already initialzed", module); + break; + } + } + + return found; +} + /* *---------------------------------------------------------------------- @@ -284,6 +326,7 @@ Ns_DriverInit(const char *server, const char *module, const Ns_DriverInitData *i { Ns_ReturnCode status = NS_OK; NsServer *servPtr = NULL; + bool alreadyInitialized = NS_FALSE; NS_NONNULL_ASSERT(module != NULL); NS_NONNULL_ASSERT(init != NULL); @@ -297,8 +340,13 @@ Ns_DriverInit(const char *server, const char *module, const Ns_DriverInitData *i Ns_Log(Bug, "cannot lookup server structure for server: %s", module); status = NS_ERROR; } + } else { + alreadyInitialized = DriverModuleInitialized(module); } + /* + * Check versions of drivers. + */ if (status == NS_OK && init->version < NS_DRIVER_VERSION_4) { Ns_Log(Warning, "%s: driver version is too old (version %d), Version 4 is recommended", module, init->version); @@ -316,7 +364,7 @@ Ns_DriverInit(const char *server, const char *module, const Ns_DriverInitData *i status = NS_ERROR; } - if (status == NS_OK) { + if (!alreadyInitialized && status == NS_OK) { const char *path, *host, *address, *bindaddr, *defserver; bool noHostNameGiven; int nrDrivers; @@ -364,7 +412,7 @@ Ns_DriverInit(const char *server, const char *module, const Ns_DriverInitData *i */ if (host == NULL) { - Tcl_DStringTrunc(&ds, 0); + Tcl_DStringSetLength(&ds, 0); if (Ns_GetHostByAddr(&ds, address) == NS_TRUE) { host = ns_strdup(Tcl_DStringValue(&ds)); @@ -408,7 +456,7 @@ Ns_DriverInit(const char *server, const char *module, const Ns_DriverInitData *i for (i = 0; i < nrDrivers; i++) { snprintf(moduleName, maxModuleNameLength, "%s:%d", module, i); status = DriverInit(server, module, moduleName, init, - servPtr, path, bindaddr, defserver, address, host, i); + servPtr, path, bindaddr, defserver, address, host); if (status != NS_OK) { break; } @@ -420,6 +468,208 @@ Ns_DriverInit(const char *server, const char *module, const Ns_DriverInitData *i return status; } + +/* + *---------------------------------------------------------------------- + * + * ServerMapEntryAdd -- + * + * Add an entry to the virtual server map. The entry consists of the + * value as provided by the host header field and location string, + * containing as well the protocol. + * + * Results: + * None + * + * Side effects: + * Potentially adding an entry to the virtual server map. + * + *---------------------------------------------------------------------- + */ + +static void +ServerMapEntryAdd(Tcl_DString *dsPtr, const char *host, const char *moduleName, + NsServer *servPtr, Driver *drvPtr, const ServerMap **defMapPtrPtr) { + Tcl_HashEntry *hPtr; + int isNew; + + NS_NONNULL_ASSERT(dsPtr != NULL); + NS_NONNULL_ASSERT(host != NULL); + NS_NONNULL_ASSERT(moduleName != NULL); + NS_NONNULL_ASSERT(servPtr != NULL); + NS_NONNULL_ASSERT(drvPtr != NULL); + + hPtr = Tcl_CreateHashEntry(&hosts, host, &isNew); + if (isNew != 0) { + ServerMap *mapPtr; + + (void) Ns_DStringVarAppend(dsPtr, drvPtr->protocol, "://", host, (char *)0); + mapPtr = ns_malloc(sizeof(ServerMap) + (size_t)dsPtr->length); + mapPtr->servPtr = servPtr; + memcpy(mapPtr->location, dsPtr->string, (size_t)dsPtr->length + 1u); + + Tcl_SetHashValue(hPtr, mapPtr); + Ns_Log(Notice, "%s: adding virtual host entry for host <%s> location: %s", + moduleName, host, mapPtr->location); + + if (defMapPtrPtr != NULL && *defMapPtrPtr == NULL) { + *defMapPtrPtr = mapPtr; + } + /* + * Always reset the DString + */ + Ns_DStringSetLength(dsPtr, 0); + } else { + Ns_Log(Notice, "===== %s: ignore duplicate virtual host entry: %s", moduleName, host); + } +} + + +/* + *---------------------------------------------------------------------- + * + * NsDriverMapVirtualServers -- + * + * Map "Host:" headers for drivers not bound to phsical servers. This + * function has to be called a time, when all servers are already defined + * such that NsGetServer(server) can succeed. + * + * Results: + * None. + * + * Side effects: + * Add an entry to the virtual server map via ServerMapEntryAdd() + * + *---------------------------------------------------------------------- + */ +void NsDriverMapVirtualServers(void) +{ + Driver *drvPtr; + Tcl_HashTable names; + + Tcl_InitHashTable(&names, TCL_STRING_KEYS); + + for (drvPtr = firstDrvPtr; drvPtr != NULL; drvPtr = drvPtr->nextPtr) { + const Ns_Set *lset; + size_t j; + int isNew = 0; + Tcl_DString ds, *dsPtr = &ds; + const char *path, *defserver, *moduleName; + + /* + * We might have multiple entries, when multiple driver threads are + * used. Skip these. + */ + (void)Tcl_CreateHashEntry(&names, drvPtr->moduleName, &isNew); + if (isNew == 0) { + continue; + } + + moduleName = drvPtr->moduleName; + defserver = drvPtr->defserver; + path = Ns_ConfigGetPath(NULL, moduleName, "servers", (char *)0); + lset = Ns_ConfigGetSection(path); + + if (lset == NULL || Ns_SetSize(lset) == 0u) { + /* + * The driver module has no ".../servers" section or an empty + * section, there are no virtual servers for this driver defined. + */ + continue; + } + + if (defserver == NULL) { + Ns_Fatal("%s: virtual servers configured," + " but %s has no defaultserver defined", moduleName, path); + } + assert(defserver != NULL); + + defMapPtr = NULL; + + Ns_DStringInit(dsPtr); + for (j = 0u; j < Ns_SetSize(lset); ++j) { + const char *server = Ns_SetKey(lset, j); + const char *host = Ns_SetValue(lset, j); + NsServer *servPtr; + + /* + * Perform an explicit lookup of the server. + */ + servPtr = NsGetServer(server); + if (servPtr == NULL) { + Ns_Log(Error, "%s: no such server: %s", moduleName, server); + } else { + char *writableHost, *hostName, *portStart; + + writableHost = ns_strdup(host); + Ns_HttpParseHost(writableHost, &hostName, &portStart); + + if (portStart == NULL) { + Tcl_DString hostDString; + + /* + * The provided host entry does NOT contain a port. + * + * Add the provided entry to the virtual server map only, + * when the configured port is the default port for the + * protocol. + */ + if (drvPtr->port == drvPtr->defport) { + ServerMapEntryAdd(dsPtr, host, moduleName, servPtr, drvPtr, + STREQ(defserver, server) ? &defMapPtr : NULL); + } + + /* + * Auto-add configured port: Add always an entry with the + * explicitly configured port of the driver. + */ + Tcl_DStringInit(&hostDString); + Tcl_DStringAppend(&hostDString, host, -1); + (void) Ns_DStringPrintf(&hostDString, ":%hu", drvPtr->port); + + ServerMapEntryAdd(dsPtr, hostDString.string, moduleName, servPtr, drvPtr, + STREQ(defserver, server) ? &defMapPtr : NULL); + + Tcl_DStringFree(&hostDString); + } else { + /* + * The provided host entry does contain a port. + * + * In case, the provided port is equal to the configured port + * of the driver, add an entry. + */ + unsigned short providedPort = (unsigned short)strtol(portStart+1, NULL, 10); + + if (providedPort == drvPtr->port) { + ServerMapEntryAdd(dsPtr, host, moduleName, servPtr, drvPtr, + STREQ(defserver, server) ? &defMapPtr : NULL); + /* + * In case, the provided port is equal to the default + * port of the driver, make sure that we have an entry + * without the port. + */ + if (providedPort == drvPtr->defport) { + ServerMapEntryAdd(dsPtr, hostName, moduleName, servPtr, drvPtr, + STREQ(defserver, server) ? &defMapPtr : NULL); + } + } else { + Ns_Log(Warning, "%s: driver is listening on port %hu; " + "virtual host entry %s ignored", + moduleName, drvPtr->port, host); + } + } + ns_free(writableHost); + } + } + Ns_DStringFree(dsPtr); + + if (defMapPtr == NULL) { + Ns_Fatal("%s: default server %s not defined in %s", moduleName, defserver, path); + } + } + Tcl_DeleteHashTable(&names); +} + /* *---------------------------------------------------------------------- @@ -442,15 +692,13 @@ static Ns_ReturnCode DriverInit(const char *server, const char *moduleName, const char *threadName, const Ns_DriverInitData *init, NsServer *servPtr, const char *path, const char *bindaddr, - const char *defserver, const char *address, const char *host, - int driverCount) + const char *defserver, const char *address, const char *host) { const char *defproto; - ServerMap *mapPtr; Driver *drvPtr; DrvWriter *wrPtr; DrvSpooler *spPtr; - int i, n; + int i; unsigned short defport; NS_NONNULL_ASSERT(threadName != NULL); @@ -496,6 +744,7 @@ DriverInit(const char *server, const char *moduleName, const char *threadName, drvPtr->type = init->name; drvPtr->moduleName = ns_strdup(moduleName); drvPtr->threadName = ns_strdup(threadName); + drvPtr->defserver = defserver; drvPtr->listenProc = init->listenProc; drvPtr->acceptProc = init->acceptProc; drvPtr->recvProc = init->recvProc; @@ -508,6 +757,7 @@ DriverInit(const char *server, const char *moduleName, const char *threadName, drvPtr->arg = init->arg; drvPtr->opts = init->opts; drvPtr->servPtr = servPtr; + drvPtr->defport = defport; drvPtr->bufsize = (size_t)Ns_ConfigIntRange(path, "bufsize", 16384, 1024, INT_MAX); drvPtr->maxinput = Ns_ConfigWideIntRange(path, "maxinput", @@ -692,60 +942,10 @@ DriverInit(const char *server, const char *moduleName, const char *threadName, threadName, wrPtr->threads); } - /* - * Map Host headers for drivers not bound to servers. - */ - - if (server == NULL && driverCount == 0) { - const Ns_Set *lset; - size_t j; - Tcl_DString ds, *dsPtr = &ds; - - if (defserver == NULL) { - Ns_Fatal("%s: virtual servers configured," - " but %s has no defaultserver defined", moduleName, path); - } - assert(defserver != NULL); - - defMapPtr = NULL; - path = Ns_ConfigGetPath(NULL, moduleName, "servers", (char *)0); - lset = Ns_ConfigGetSection(path); - - Ns_DStringInit(dsPtr); - for (j = 0u; lset != NULL && j < Ns_SetSize(lset); ++j) { - server = Ns_SetKey(lset, j); - host = Ns_SetValue(lset, j); - servPtr = NsGetServer(server); - if (servPtr == NULL) { - Ns_Log(Error, "%s: no such server: %s", moduleName, server); - } else { - Tcl_HashEntry *hPtr = Tcl_CreateHashEntry(&hosts, host, &n); - - if (n == 0) { - Ns_Log(Error, "%s: duplicate host map: %s", moduleName, host); - } else { - (void) Ns_DStringVarAppend(dsPtr, drvPtr->protocol, "://", host, (char *)0); - mapPtr = ns_malloc(sizeof(ServerMap) + (size_t)ds.length); - mapPtr->servPtr = servPtr; - memcpy(mapPtr->location, ds.string, (size_t)ds.length + 1u); - Ns_DStringSetLength(&ds, 0); - if (defMapPtr == NULL && STREQ(defserver, server)) { - defMapPtr = mapPtr; - } - Tcl_SetHashValue(hPtr, mapPtr); - } - } - } - Ns_DStringFree(dsPtr); - - if (defMapPtr == NULL) { - Ns_Fatal("%s: default server %s not defined in %s", moduleName, server, path); - } - } - return NS_OK; } + /* *---------------------------------------------------------------------- @@ -2702,24 +2902,33 @@ SockSendResponse(Sock *sockPtr, int code, const char *errMsg) if (sockPtr->reqPtr != NULL) { Request *reqPtr = sockPtr->reqPtr; - Tcl_DString dsReqLine; const char *requestLine = (reqPtr->request.line != NULL) ? reqPtr->request.line : ""; (void)ns_inet_ntop((struct sockaddr *)&(sockPtr->sa), sockPtr->reqPtr->peer, NS_IPADDR_SIZE); - Tcl_DStringInit(&dsReqLine); - Ns_Log(Warning, "invalid request: %d (%s) from peer %s request '%s' offsets: read %lu write %lu content %lu, avail %lu", - code, errMsg, - reqPtr->peer, - Ns_DStringAppendPrintable(&dsReqLine, requestLine, strlen(requestLine)), - reqPtr->roff, - reqPtr->woff, - reqPtr->coff, - reqPtr->avail); - Tcl_DStringFree(&dsReqLine); + /* + * Check, if bad request looks like a TLS handshake. If yes, there is + * no need to print out the received buffer. + */ + if (requestLine[0] == (char)0x16 && requestLine[1] >= 3 && requestLine[2] == 1) { + Ns_Log(Warning, "invalid request %d (%s) from peer %s: recveived TLS handshake on a non-TLS connection", + code, errMsg, reqPtr->peer); + } else { + Tcl_DString dsReqLine; - LogBuffer(Warning, "REQ BUFFER", reqPtr->buffer.string, (size_t)reqPtr->buffer.length); + Tcl_DStringInit(&dsReqLine); + Ns_Log(Warning, "invalid request: %d (%s) from peer %s request '%s' offsets: read %lu write %lu content %lu, avail %lu", + code, errMsg, + reqPtr->peer, + Ns_DStringAppendPrintable(&dsReqLine, requestLine, strlen(requestLine)), + reqPtr->roff, + reqPtr->woff, + reqPtr->coff, + reqPtr->avail); + Tcl_DStringFree(&dsReqLine); + LogBuffer(Warning, "REQ BUFFER", reqPtr->buffer.string, (size_t)reqPtr->buffer.length); + } } else { Ns_Log(Warning, "invalid request: %d (%s) - no request information available", code, errMsg); @@ -2816,8 +3025,8 @@ SockClose(Sock *sockPtr, int keep) */ if (sockPtr->taddr != NULL) { munmap(sockPtr->taddr, (size_t)sockPtr->tsize); + sockPtr->taddr = NULL; } - sockPtr->taddr = NULL; #endif } @@ -3216,7 +3425,7 @@ EndOfHeader(Sock *sockPtr) * Handle too large input requests */ if (reqPtr->length > (size_t)sockPtr->drvPtr->maxinput) { - Ns_Log(DriverDebug, "SockParse: request too large, length=%" + Ns_Log(Notice, "SockParse: request too large, length=%" PRIdz ", maxinput=%" TCL_LL_MODIFIER "d", reqPtr->length, sockPtr->drvPtr->maxinput); /* @@ -3612,14 +3821,12 @@ SockParse(Sock *sockPtr) static void SockSetServer(Sock *sockPtr) { - const ServerMap *mapPtr = NULL; char *host; - int status = 1; Request *reqPtr; + bool bad_request = NS_FALSE; NS_NONNULL_ASSERT(sockPtr != NULL); - reqPtr = sockPtr->reqPtr; assert(reqPtr != NULL); @@ -3635,9 +3842,11 @@ SockSetServer(Sock *sockPtr) */ Ns_Log(Notice, "request header field \"Host\" is missing in HTTP/1.1 request: \"%s\"\n", reqPtr->request.line); - status = 0; + bad_request = NS_TRUE; } if (sockPtr->servPtr == NULL) { + const ServerMap *mapPtr = NULL; + if (host != NULL) { const Tcl_HashEntry *hPtr; size_t hostLength = strlen(host); @@ -3652,6 +3861,10 @@ SockSetServer(Sock *sockPtr) hPtr = Tcl_FindHashEntry(&hosts, host); if (hPtr != NULL) { mapPtr = Tcl_GetHashValue(hPtr); + } else { + Ns_Log(DriverDebug, + "cannot locate host header content '%s' in virtual hosts table, fall back to default '%s'", + host, defMapPtr->location); } } if (mapPtr == NULL) { @@ -3664,11 +3877,11 @@ SockSetServer(Sock *sockPtr) if (sockPtr->servPtr == NULL) { Ns_Log(Warning, "cannot determine server for request: \"%s\" (host \"%s\")\n", reqPtr->request.line, host); - status = 0; + bad_request = NS_TRUE; } } - if (unlikely(status == 0)) { + if (unlikely(bad_request)) { Ns_Log(DriverDebug, "SockSetServer sets method to BAD"); ns_free((char *)reqPtr->request.method); reqPtr->request.method = ns_strdup("BAD"); @@ -4674,7 +4887,7 @@ NsWriterQueue(Ns_Conn *conn, size_t nsend, Tcl_Channel chan, FILE *fp, int fd, NS_NONNULL_ASSERT(conn != NULL); connPtr = (Conn *)conn; - + if (unlikely(connPtr->sockPtr == NULL)) { Ns_Log(Warning, "NsWriterQueue: called without sockPtr size %" PRIdz " bufs %d flags %.6x stream %.6x chan %p fd %d", @@ -4706,7 +4919,7 @@ NsWriterQueue(Ns_Conn *conn, size_t nsend, Tcl_Channel chan, FILE *fp, int fd, } assert(wrPtr != NULL); - + if (((connPtr->flags & NS_CONN_STREAM) != 0u) || connPtr->fd > 0) { bool first = NS_FALSE; size_t wrote = 0u; @@ -4722,7 +4935,7 @@ NsWriterQueue(Ns_Conn *conn, size_t nsend, Tcl_Channel chan, FILE *fp, int fd, } else { Ns_Log(DriverDebug, "NsWriterQueue: streaming writer job"); - + if (connPtr->fd == 0) { /* * Create a new temporary spool file. @@ -4731,7 +4944,7 @@ NsWriterQueue(Ns_Conn *conn, size_t nsend, Tcl_Channel chan, FILE *fp, int fd, fd = connPtr->fd = Ns_GetTemp(); Ns_Log(DriverDebug, "NsWriterQueue: new tmp file has fd %d", fd); - + } else { /* * Reuse previously created spool file. @@ -4741,7 +4954,7 @@ NsWriterQueue(Ns_Conn *conn, size_t nsend, Tcl_Channel chan, FILE *fp, int fd, Ns_Log(Notice, "NsWriterQueue: writer job was already canceled; maybe user dropped connection."); status = NS_ERROR; - + } else { Ns_MutexLock(&wrSockPtr1->c.file.fdlock); (void)ns_lseek(connPtr->fd, 0, SEEK_END); @@ -5234,9 +5447,8 @@ WriterSubmitFileObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int ob Tcl_SetObjResult(interp, Tcl_NewBooleanObj(status == NS_OK ? 1 : 0)); (void) ns_close(fd); - } - if (fd != NS_INVALID_FD) { + } else if (fd != NS_INVALID_FD) { (void) ns_close(fd); } } @@ -5347,6 +5559,7 @@ WriterSizeObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tc { int intValue = -1, result = TCL_OK; Tcl_Obj *driverObj; + DrvWriter *wrPtr; Ns_ObjvSpec args[] = { {"driver", Ns_ObjvObj, &driverObj, NULL}, {"?value", Ns_ObjvInt, &intValue, NULL}, @@ -5357,14 +5570,18 @@ WriterSizeObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tc result = TCL_ERROR; } else { - DrvWriter *wrPtr = DriverWriterFromObj(driverObj); + wrPtr = DriverWriterFromObj(driverObj); if (unlikely(wrPtr == NULL)) { Ns_TclPrintfResult(interp, "no writer configured for a driver with name %s", Tcl_GetString(driverObj)); result = TCL_ERROR; - } else if (objc == 4) { + } + } + if (result == TCL_OK) { + assert(wrPtr != NULL); + if (objc == 4) { /* * The optional argument was provided. */ @@ -5401,7 +5618,8 @@ WriterSizeObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tc *---------------------------------------------------------------------- */ static int -WriterStreamingObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) +WriterStreamingObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, + int objc, Tcl_Obj *CONST* objv) { int boolValue, result = TCL_OK; Tcl_Obj *driverObj; @@ -5418,7 +5636,7 @@ WriterStreamingObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int obj DrvWriter *wrPtr = DriverWriterFromObj(driverObj); if (unlikely(wrPtr == NULL)) { - Ns_TclPrintfResult(interp, "no writer configured for a driver with name %s", + Ns_TclPrintfResult(interp, "no writer configured for driver '%s'", Tcl_GetString(driverObj)); result = TCL_ERROR; @@ -5456,7 +5674,8 @@ WriterStreamingObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int obj *---------------------------------------------------------------------- */ int -NsTclWriterObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) +NsTclWriterObjCmd(ClientData clientData, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST* objv) { const Ns_SubCmdSpec subcmds[] = { {"list", WriterListObjCmd}, @@ -5900,6 +6119,57 @@ AsyncWriterThread(void *arg) } + +/* + *---------------------------------------------------------------------- + * + * LookupDriver -- + * + * Find a matching driver for the specified protocol and optionally the + * specified driver name. + * + * Results: + * Driver pointer or NULL on failure. + * + * Side effects: + * When no driver is found, an error ist left int the interp result. + * + *---------------------------------------------------------------------- + */ +static Driver * +LookupDriver(Tcl_Interp *interp, const char* protocol, const char *driverName) +{ + Driver *drvPtr; + + for (drvPtr = firstDrvPtr; drvPtr != NULL; drvPtr = drvPtr->nextPtr) { + Ns_Log(DriverDebug, "... check Driver proto <%s> server %s name %s location %s", + drvPtr->protocol, drvPtr->server, drvPtr->threadName, drvPtr->location); + + if (STREQ(drvPtr->protocol, protocol)) { + if (driverName == NULL) { + /* + * If there is no driver name given, take the first driver + * with the matching protocol. + */ + break; + } else if (STREQ(drvPtr->moduleName, driverName)) { + /* + * The driver name (name of the loaded module) is equal + */ + break; + } + } + } + + if (drvPtr == NULL) { + if (driverName != NULL) { + Ns_TclPrintfResult(interp, "no driver for protocol '%s' & driver name '%s' found", protocol, driverName); + } else { + Ns_TclPrintfResult(interp, "no driver for protocol '%s' found", protocol); + } + } + return drvPtr; +} /* *---------------------------------------------------------------------- * @@ -5948,30 +6218,8 @@ NSDriverClientOpen(Tcl_Interp *interp, const char *driverName, * Find a matching driver for the specified protocol and optionally * the specified driver name. */ - for (drvPtr = firstDrvPtr; drvPtr != NULL; drvPtr = drvPtr->nextPtr) { - Ns_Log(DriverDebug, "... check Driver proto <%s> server %s name %s location %s", - drvPtr->protocol, drvPtr->server, drvPtr->threadName, drvPtr->location); - if (STREQ(drvPtr->protocol, protocol)) { - if (driverName == NULL) { - /* - * If there is no driver name given, take the first driver - * with the matching protocol. - */ - break; - } else if (STREQ(drvPtr->moduleName, driverName)) { - /* - * The driver name (name of the loaded module) is equal - */ - break; - } - } - } + drvPtr = LookupDriver(interp, protocol, driverName); if (drvPtr == NULL) { - if (driverName != NULL) { - Ns_TclPrintfResult(interp, "no driver for protocol '%s' & driver name '%s' found", protocol, driverName); - } else { - Ns_TclPrintfResult(interp, "no driver for protocol '%s' found", protocol); - } result = TCL_ERROR; } else if (portString != NULL) { @@ -6002,8 +6250,11 @@ NSDriverClientOpen(Tcl_Interp *interp, const char *driverName, const char *query; Tcl_DString ds, *dsPtr = &ds; Request *reqPtr; - Sock *sockPtr = SockNew(drvPtr); + Sock *sockPtr; + + assert(drvPtr != NULL); + sockPtr = SockNew(drvPtr); sockPtr->sock = sock; sockPtr->servPtr = drvPtr->servPtr; if (sockPtr->servPtr == NULL) { @@ -6053,6 +6304,69 @@ NSDriverClientOpen(Tcl_Interp *interp, const char *driverName, return result; } +/* + *---------------------------------------------------------------------- + * + * NSDriverSockNew -- + * + * Create a Sock structure based on the driver interface + * + * Results: + * Tcl return code. + * + * Side effects: + * Accepting a connection + * + *---------------------------------------------------------------------- + */ + +int +NSDriverSockNew(Tcl_Interp *interp, NS_SOCKET sock, + const char *protocol, const char *driverName, const char *methodName, + Sock **sockPtrPtr) +{ + int result = TCL_OK; + Driver *drvPtr; + Sock *sockPtr = NULL; + + NS_NONNULL_ASSERT(interp != NULL); + NS_NONNULL_ASSERT(protocol != NULL); + NS_NONNULL_ASSERT(methodName != NULL); + NS_NONNULL_ASSERT(sockPtrPtr != NULL); + + drvPtr = LookupDriver(interp, protocol, driverName); + if (drvPtr == NULL) { + result = TCL_ERROR; + } else { + Tcl_DString ds, *dsPtr = &ds; + Request *reqPtr; + + sockPtr = SockNew(drvPtr); + sockPtr->servPtr = drvPtr->servPtr; + sockPtr->sock = sock; + + RequestNew(sockPtr); // not sure if needed + // peerAddr is missing + + Ns_GetTime(&sockPtr->acceptTime); + reqPtr = sockPtr->reqPtr; + + Tcl_DStringInit(dsPtr); + Ns_DStringAppend(dsPtr, methodName); + Ns_StrToUpper(Ns_DStringValue(dsPtr)); + + reqPtr->request.line = Ns_DStringExport(dsPtr); + reqPtr->request.method = ns_strdup(methodName); + reqPtr->request.protocol = ns_strdup(protocol); + reqPtr->request.host = NULL; + reqPtr->request.query = NULL; + Ns_Log(Notice, "REQUEST LINE <%s> query <%s>", reqPtr->request.line, reqPtr->request.query); + + *sockPtrPtr = sockPtr; + } + + return result; +} /* * Local Variables: diff --git a/nsd/dstring.c b/nsd/dstring.c index 8e83e5c7..ba296855 100644 --- a/nsd/dstring.c +++ b/nsd/dstring.c @@ -425,7 +425,7 @@ Ns_DStringAppendTime(Tcl_DString *dsPtr, const Ns_Time *timePtr) return Ns_DStringPrintf(dsPtr, "%" PRIu64 ".%06ld", (int64_t)timePtr->sec, timePtr->usec); } - + /* *---------------------------------------------------------------------- @@ -474,7 +474,7 @@ Ns_DStringSetLength(Ns_DString *dsPtr, int length) #undef Ns_DStringTrunc -NS_EXTERN void Ns_DStringTrunc(Ns_DString *dsPtr, int length) NS_GNUC_DEPRECATED_FOR(Tcl_DStringTrunc); +NS_EXTERN void Ns_DStringTrunc(Ns_DString *dsPtr, int length) NS_GNUC_DEPRECATED_FOR(Tcl_DStringSetLength); void Ns_DStringTrunc(Ns_DString *dsPtr, int length) diff --git a/nsd/encoding.c b/nsd/encoding.c index 892291df..33b23864 100644 --- a/nsd/encoding.c +++ b/nsd/encoding.c @@ -217,47 +217,57 @@ NsConfigEncodings(void) static Ns_ReturnCode ConfigServerEncodings(const char *server) { - NsServer *servPtr = NsGetServer(server); - const char *path; + NsServer *servPtr = NsGetServer(server); + Ns_ReturnCode result; - /* - * Configure the encoding used in the request URL. - */ + if (unlikely(servPtr == NULL)) { + Ns_Log(Warning, "Could not set encoding, server '%s' unknown", server); + result = NS_ERROR; + + } else { + const char *path; - path = Ns_ConfigGetPath(server, NULL, (char *)0); + /* + * Configure the encoding used in the request URL. + */ - servPtr->encoding.urlCharset = - Ns_ConfigString(path, "urlCharset", "utf-8"); + path = Ns_ConfigGetPath(server, NULL, (char *)0); - servPtr->encoding.urlEncoding = - Ns_GetCharsetEncoding(servPtr->encoding.urlCharset); - if (servPtr->encoding.urlEncoding == NULL) { - Ns_Log(Warning, "no encoding found for charset \"%s\" from config", - servPtr->encoding.urlCharset); - } + servPtr->encoding.urlCharset = + Ns_ConfigString(path, "urlCharset", "utf-8"); - /* - * Configure the encoding used for Tcl/ADP output. - */ + servPtr->encoding.urlEncoding = + Ns_GetCharsetEncoding(servPtr->encoding.urlCharset); + if (servPtr->encoding.urlEncoding == NULL) { + Ns_Log(Warning, "no encoding found for charset \"%s\" from config", + servPtr->encoding.urlCharset); + } - servPtr->encoding.outputCharset = - Ns_ConfigString(path, "outputCharset", "utf-8"); + /* + * Configure the encoding used for Tcl/ADP output. + */ - servPtr->encoding.outputEncoding = - Ns_GetCharsetEncoding(servPtr->encoding.outputCharset); - if (servPtr->encoding.outputEncoding == NULL) { - Ns_Fatal("could not find encoding for default output charset \"%s\"", - servPtr->encoding.outputCharset); - } + servPtr->encoding.outputCharset = + Ns_ConfigString(path, "outputCharset", "utf-8"); - /* - * Force charset into content-type header for dynamic responses. - */ + servPtr->encoding.outputEncoding = + Ns_GetCharsetEncoding(servPtr->encoding.outputCharset); + if (servPtr->encoding.outputEncoding == NULL) { + Ns_Fatal("could not find encoding for default output charset \"%s\"", + servPtr->encoding.outputCharset); + } + + /* + * Force charset into content-type header for dynamic responses. + */ - servPtr->encoding.hackContentTypeP = - Ns_ConfigBool(path, "HackContentType", NS_TRUE); + servPtr->encoding.hackContentTypeP = + Ns_ConfigBool(path, "HackContentType", NS_TRUE); - return NS_OK; + result = NS_OK; + + } + return result; } diff --git a/nsd/event.c b/nsd/event.c index 5e2fc12a..a38a1edc 100644 --- a/nsd/event.c +++ b/nsd/event.c @@ -60,7 +60,7 @@ typedef struct Event { */ typedef struct EventQueue { - Event *firstInitPtr; /* New events to be initialised. */ + Event *firstInitPtr; /* New events to be initialized. */ Event *firstWaitPtr; /* Sockets waiting for events or timeout. */ Event *firstFreePtr; /* Free, unused Event structs. */ struct pollfd *pfds; /* Array of pollfd structs. */ diff --git a/nsd/exec.c b/nsd/exec.c index e1f2d0c5..e9b55785 100644 --- a/nsd/exec.c +++ b/nsd/exec.c @@ -120,7 +120,7 @@ Ns_ExecProc(const char *exec, char **argv) Ns_ReturnCode Ns_WaitProcess(pid_t pid) { - return Ns_WaitForProcess(pid, NULL); + return Ns_WaitForProcessStatus(pid, NULL, NULL); } @@ -138,13 +138,19 @@ Ns_WaitProcess(pid_t pid) * *---------------------------------------------------------------------- */ - Ns_ReturnCode Ns_WaitForProcess(pid_t pid, int *exitcodePtr) { + return Ns_WaitForProcessStatus(pid, exitcodePtr, NULL); +} + + +Ns_ReturnCode +Ns_WaitForProcessStatus(pid_t pid, int *exitcodePtr, int *waitstatusPtr) +{ + Ns_ReturnCode status = NS_OK; #ifdef _WIN32 HANDLE process = (HANDLE) pid; - Ns_ReturnCode status = NS_OK; DWORD exitcode = 0u; if ((WaitForSingleObject(process, INFINITE) == WAIT_FAILED) || @@ -168,32 +174,35 @@ Ns_WaitForProcess(pid_t pid, int *exitcodePtr) status = NS_ERROR; } } - return status; #else - int status = 0; + int waitstatus = 0; pid_t p; do { - p = waitpid(pid, &status, 0); + p = waitpid(pid, &waitstatus, 0); } while (p != pid && errno == NS_EINTR); + if (p != pid) { Ns_Log(Error, "waitpid(%d) failed: %s", pid, strerror(errno)); - return NS_ERROR; - } - if (WIFSIGNALED(status)) { + status = NS_ERROR; + + } else if (WIFSIGNALED(waitstatus)) { const char *coredump = ""; #ifdef WCOREDUMP - if (WCOREDUMP(status)) { + if (WCOREDUMP(waitstatus)) { coredump = " - core dumped"; } #endif - Ns_Log(Error, "process %d killed with signal %d (%s)%s", pid, - WTERMSIG(status), strsignal(WTERMSIG(status)), coredump); - } else if (!WIFEXITED(status)) { - Ns_Log(Error, "waitpid(%d): invalid status: %d", pid, status); + if (*coredump != '\0' || waitstatusPtr == NULL) { + Ns_Log(Error, "process %d killed with signal %d (%s)%s", pid, + WTERMSIG(waitstatus), strsignal(WTERMSIG(waitstatus)), coredump); + } + } else if (!WIFEXITED(waitstatus)) { + Ns_Log(Error, "waitpid(%d): invalid status: %d", pid, waitstatus); } else { - int exitcode = WEXITSTATUS(status); + int exitcode = WEXITSTATUS(waitstatus); + if (exitcode != 0) { Ns_Log(Warning, "process %d exited with non-zero exit code: %d", pid, exitcode); @@ -202,8 +211,14 @@ Ns_WaitForProcess(pid_t pid, int *exitcodePtr) *exitcodePtr = exitcode; } } - return NS_OK; + + if (waitstatusPtr != NULL) { + *waitstatusPtr = waitstatus; + } + #endif /* _WIN32 */ + + return status; } diff --git a/nsd/fastpath.c b/nsd/fastpath.c index 13370442..60050c25 100644 --- a/nsd/fastpath.c +++ b/nsd/fastpath.c @@ -197,50 +197,59 @@ NormalizePath(const char **pathPtr) { static Ns_ReturnCode ConfigServerFastpath(const char *server) { - NsServer *servPtr = NsGetServer(server); - Ns_DString ds; - const char *path, *p; + NsServer *servPtr = NsGetServer(server); + Ns_ReturnCode result; - path = Ns_ConfigGetPath(server, NULL, "fastpath", (char *)0); - Ns_DStringInit(&ds); + if (unlikely(servPtr == NULL)) { + Ns_Log(Warning, "Could configure fastpath; server '%s' unknown", server); + result = NS_ERROR; - p = Ns_ConfigString(path, "directoryfile", "index.adp index.tcl index.html index.htm"); - if (p != NULL && Tcl_SplitList(NULL, p, &servPtr->fastpath.dirc, - &servPtr->fastpath.dirv) != TCL_OK) { - Ns_Log(Error, "fastpath[%s]: directoryfile is not a list: %s", server, p); - } + } else { + Ns_DString ds; + const char *path, *p; - servPtr->fastpath.serverdir = Ns_ConfigString(path, "serverdir", ""); - if (Ns_PathIsAbsolute(servPtr->fastpath.serverdir) == NS_FALSE) { - (void)Ns_HomePath(&ds, servPtr->fastpath.serverdir, (char *)0); - servPtr->fastpath.serverdir = Ns_DStringExport(&ds); - } else { - NormalizePath(&servPtr->fastpath.serverdir); - } + path = Ns_ConfigGetPath(server, NULL, "fastpath", (char *)0); + Ns_DStringInit(&ds); - /* - * Not sure, we still need fastpath.pageroot AND fastpath.pagedir. - * "pageroot" always points to the absolute path, while "pagedir" - * might contain the relative path (or is the same as "pageroot"). - */ - servPtr->fastpath.pagedir = Ns_ConfigString(path, "pagedir", "pages"); - if (Ns_PathIsAbsolute(servPtr->fastpath.pagedir) == NS_TRUE) { - servPtr->fastpath.pageroot = servPtr->fastpath.pagedir; - NormalizePath(&servPtr->fastpath.pageroot); - } else { - (void)Ns_MakePath(&ds, servPtr->fastpath.serverdir, - servPtr->fastpath.pagedir, (char *)0); - servPtr->fastpath.pageroot = Ns_DStringExport(&ds); - } - - servPtr->fastpath.dirproc = Ns_ConfigString(path, "directoryproc", "_ns_dirlist"); - servPtr->fastpath.diradp = Ns_ConfigGetValue(path, "directoryadp"); + p = Ns_ConfigString(path, "directoryfile", "index.adp index.tcl index.html index.htm"); + if (p != NULL && Tcl_SplitList(NULL, p, &servPtr->fastpath.dirc, + &servPtr->fastpath.dirv) != TCL_OK) { + Ns_Log(Error, "fastpath[%s]: directoryfile is not a list: %s", server, p); + } - Ns_RegisterRequest(server, "GET", "/", Ns_FastPathProc, NULL, NULL, 0u); - Ns_RegisterRequest(server, "HEAD", "/", Ns_FastPathProc, NULL, NULL, 0u); - Ns_RegisterRequest(server, "POST", "/", Ns_FastPathProc, NULL, NULL, 0u); + servPtr->fastpath.serverdir = Ns_ConfigString(path, "serverdir", ""); + if (Ns_PathIsAbsolute(servPtr->fastpath.serverdir) == NS_FALSE) { + (void)Ns_HomePath(&ds, servPtr->fastpath.serverdir, (char *)0); + servPtr->fastpath.serverdir = Ns_DStringExport(&ds); + } else { + NormalizePath(&servPtr->fastpath.serverdir); + } - return NS_OK; + /* + * Not sure, we still need fastpath.pageroot AND fastpath.pagedir. + * "pageroot" always points to the absolute path, while "pagedir" + * might contain the relative path (or is the same as "pageroot"). + */ + servPtr->fastpath.pagedir = Ns_ConfigString(path, "pagedir", "pages"); + if (Ns_PathIsAbsolute(servPtr->fastpath.pagedir) == NS_TRUE) { + servPtr->fastpath.pageroot = servPtr->fastpath.pagedir; + NormalizePath(&servPtr->fastpath.pageroot); + } else { + (void)Ns_MakePath(&ds, servPtr->fastpath.serverdir, + servPtr->fastpath.pagedir, (char *)0); + servPtr->fastpath.pageroot = Ns_DStringExport(&ds); + } + + servPtr->fastpath.dirproc = Ns_ConfigString(path, "directoryproc", "_ns_dirlist"); + servPtr->fastpath.diradp = Ns_ConfigGetValue(path, "directoryadp"); + + Ns_RegisterRequest(server, "GET", "/", Ns_FastPathProc, NULL, NULL, 0u); + Ns_RegisterRequest(server, "HEAD", "/", Ns_FastPathProc, NULL, NULL, 0u); + Ns_RegisterRequest(server, "POST", "/", Ns_FastPathProc, NULL, NULL, 0u); + + result = NS_OK; + } + return result; } diff --git a/nsd/filter.c b/nsd/filter.c index 9f90254a..3f88f2cd 100644 --- a/nsd/filter.c +++ b/nsd/filter.c @@ -377,66 +377,68 @@ NewTrace(Ns_TraceProc *proc, void *arg) void NsGetFilters(Tcl_DString *dsPtr, const char *server) { - const Filter *fPtr; const NsServer *servPtr; NS_NONNULL_ASSERT(dsPtr != NULL); NS_NONNULL_ASSERT(server != NULL); servPtr = NsGetServer(server); - assert(servPtr != NULL); - - fPtr = servPtr->filter.firstFilterPtr; - while (fPtr != NULL) { - Tcl_DStringStartSublist(dsPtr); - Tcl_DStringAppendElement(dsPtr, fPtr->method); - Tcl_DStringAppendElement(dsPtr, fPtr->url); - switch (fPtr->when) { - case NS_FILTER_PRE_AUTH: - Tcl_DStringAppendElement(dsPtr, "preauth"); - break; - case NS_FILTER_POST_AUTH: - Tcl_DStringAppendElement(dsPtr, "postauth"); - break; - case NS_FILTER_VOID_TRACE: - case NS_FILTER_TRACE: - Tcl_DStringAppendElement(dsPtr, "trace"); - break; + + if (servPtr != NULL) { + const Filter *fPtr; + + for (fPtr = servPtr->filter.firstFilterPtr; fPtr != NULL; fPtr = fPtr->nextPtr) { + Tcl_DStringStartSublist(dsPtr); + Tcl_DStringAppendElement(dsPtr, fPtr->method); + Tcl_DStringAppendElement(dsPtr, fPtr->url); + + switch (fPtr->when) { + case NS_FILTER_PRE_AUTH: + Tcl_DStringAppendElement(dsPtr, "preauth"); + break; + case NS_FILTER_POST_AUTH: + Tcl_DStringAppendElement(dsPtr, "postauth"); + break; + case NS_FILTER_VOID_TRACE: + case NS_FILTER_TRACE: + Tcl_DStringAppendElement(dsPtr, "trace"); + break; + } + Ns_GetProcInfo(dsPtr, (Ns_Callback *)fPtr->proc, fPtr->arg); + Tcl_DStringEndSublist(dsPtr); } - Ns_GetProcInfo(dsPtr, (Ns_Callback *)fPtr->proc, fPtr->arg); - Tcl_DStringEndSublist(dsPtr); - fPtr = fPtr->nextPtr; } } void NsGetTraces(Tcl_DString *dsPtr, const char *server) { - const Trace *tracePtr; const NsServer *servPtr; NS_NONNULL_ASSERT(dsPtr != NULL); NS_NONNULL_ASSERT(server != NULL); servPtr = NsGetServer(server); - assert(servPtr != NULL); + if (likely(servPtr != NULL)) { + const Trace *tracePtr; - tracePtr = servPtr->filter.firstTracePtr; - while (tracePtr != NULL) { - Tcl_DStringStartSublist(dsPtr); - Tcl_DStringAppendElement(dsPtr, "trace"); - Ns_GetProcInfo(dsPtr, (Ns_Callback *)tracePtr->proc, tracePtr->arg); - Tcl_DStringEndSublist(dsPtr); - tracePtr = tracePtr->nextPtr; - } + tracePtr = servPtr->filter.firstTracePtr; + while (tracePtr != NULL) { + Tcl_DStringStartSublist(dsPtr); + Tcl_DStringAppendElement(dsPtr, "trace"); + Ns_GetProcInfo(dsPtr, (Ns_Callback *)tracePtr->proc, tracePtr->arg); + Tcl_DStringEndSublist(dsPtr); + tracePtr = tracePtr->nextPtr; + } - tracePtr = servPtr->filter.firstCleanupPtr; - while (tracePtr != NULL) { - Tcl_DStringStartSublist(dsPtr); - Tcl_DStringAppendElement(dsPtr, "cleanup"); - Ns_GetProcInfo(dsPtr, (Ns_Callback *)tracePtr->proc, tracePtr->arg); - Tcl_DStringEndSublist(dsPtr); - tracePtr = tracePtr->nextPtr; + tracePtr = servPtr->filter.firstCleanupPtr; + while (tracePtr != NULL) { + Tcl_DStringStartSublist(dsPtr); + Tcl_DStringAppendElement(dsPtr, "cleanup"); + Ns_GetProcInfo(dsPtr, (Ns_Callback *)tracePtr->proc, tracePtr->arg); + Tcl_DStringEndSublist(dsPtr); + tracePtr = tracePtr->nextPtr; + } } } diff --git a/nsd/form.c b/nsd/form.c index 5c3f67ee..28877d5a 100644 --- a/nsd/form.c +++ b/nsd/form.c @@ -39,7 +39,7 @@ * Local functions defined in this file. */ -static void ParseQuery(char *form, Ns_Set *set, Tcl_Encoding encoding) +static void ParseQuery(char *form, Ns_Set *set, Tcl_Encoding encoding, bool translate) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2); static void ParseMultiInput(Conn *connPtr, const char *start, char *end) @@ -97,7 +97,7 @@ Ns_ConnGetQuery(Ns_Conn *conn) */ form = connPtr->request.query; if (form != NULL) { - ParseQuery(form, connPtr->query, connPtr->urlEncoding); + ParseQuery(form, connPtr->query, connPtr->urlEncoding, NS_FALSE); } } else if (/* * It is unsafe to access the content when the @@ -107,10 +107,11 @@ Ns_ConnGetQuery(Ns_Conn *conn) (connPtr->flags & NS_CONN_CLOSED ) == 0u && (form = connPtr->reqPtr->content) != NULL ) { - Tcl_DString bound; - const char *contentType = Ns_SetIGet(conn->headers, "content-type"); + const char *contentType = Ns_SetIGet(conn->headers, "content-type"); if (contentType != NULL) { + Tcl_DString bound; + Tcl_DStringInit(&bound); /* @@ -118,7 +119,20 @@ Ns_ConnGetQuery(Ns_Conn *conn) */ if (!GetBoundary(&bound, contentType)) { if (Ns_StrCaseFind(contentType, "www-form-urlencoded") != NULL) { - ParseQuery(form, connPtr->query, connPtr->urlEncoding); + bool translate; +#ifdef _WIN32 + /* + * Keep CRLF + */ + translate = NS_FALSE; +#else + /* + * Translate CRLF -> LF, since browsers translate all + * LF to CRLF in the body of POST requests. + */ + translate = NS_TRUE; +#endif + ParseQuery(form, connPtr->query, connPtr->urlEncoding, translate); } /* * Don't do anything for other content-types. @@ -232,7 +246,7 @@ Ns_QueryToSet(char *query, Ns_Set *set) NS_NONNULL_ASSERT(query != NULL); NS_NONNULL_ASSERT(set != NULL); - ParseQuery(query, set, NULL); + ParseQuery(query, set, NULL, NS_FALSE); return NS_OK; } @@ -296,20 +310,21 @@ NsTclParseQueryObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int obj */ static void -ParseQuery(char *form, Ns_Set *set, Tcl_Encoding encoding) +ParseQuery(char *form, Ns_Set *set, Tcl_Encoding encoding, bool translate) { - Tcl_DString kds, vds; - char *p; + Tcl_DString kds, vds, vds2; + char *p; NS_NONNULL_ASSERT(form != NULL); NS_NONNULL_ASSERT(set != NULL); Tcl_DStringInit(&kds); Tcl_DStringInit(&vds); + Tcl_DStringInit(&vds2); p = form; while (p != NULL) { - char *v; + char *v; const char *k; k = p; @@ -328,6 +343,27 @@ ParseQuery(char *form, Ns_Set *set, Tcl_Encoding encoding) (void) Ns_UrlQueryDecode(&vds, v+1, encoding); *v = '='; v = vds.string; + if (translate) { + char *q = strchr(v, INTCHAR('\r')); + + if (q != NULL) { + /* + * We have one or more CR in the field content. + * Remove these. + */ + Ns_DStringSetLength(&vds2, 0); + do { + Tcl_DStringAppend(&vds2, v, (int)(q - v)); + v = q +1; + q = strchr(v, INTCHAR('\r')); + } while (q != NULL); + /* + * Append the remaining string. + */ + Tcl_DStringAppend(&vds2, v, -1); + v = vds2.string; + } + } } (void) Ns_SetPut(set, k, v); if (p != NULL) { @@ -336,6 +372,7 @@ ParseQuery(char *form, Ns_Set *set, Tcl_Encoding encoding) } Tcl_DStringFree(&kds); Tcl_DStringFree(&vds); + Tcl_DStringFree(&vds2); } diff --git a/nsd/init.tcl b/nsd/init.tcl index b0f8fc8e..2f4beb7d 100644 --- a/nsd/init.tcl +++ b/nsd/init.tcl @@ -220,11 +220,12 @@ ns_ictl trace allocate ns_init ns_ictl trace deallocate ns_cleanup proc ns_cleanup {} { - ns_cleanupchans; # Close files - ns_cleanupvars; # Destroy global variables - ns_set cleanup; # Destroy non-shared sets - ns_http cleanup; # Abort any http requests - ns_ictl cleanup; # Run depreciated 1-shot Ns_TclRegisterDefer's. + ns_cache_transaction_rollback -all ;# in case, ns_cache_transactions are not terminated correctly, do it now + ns_cleanupchans ;# Close files + ns_cleanupvars ;# Destroy global variables + ns_set cleanup ;# Destroy non-shared sets + ns_http cleanup ;# Abort any http requests + ns_ictl cleanup ; # Run depreciated 1-shot Ns_TclRegisterDefer's. } # diff --git a/nsd/listen.c b/nsd/listen.c index 50c4e5a4..bf7c0913 100644 --- a/nsd/listen.c +++ b/nsd/listen.c @@ -11,7 +11,7 @@ * * The Original Code is AOLserver Code and related documentation * distributed by AOL. - * + * * The Initial Developer of the Original Code is America Online, * Inc. Portions created by AOL are Copyright (C) 1999 America Online, * Inc. All Rights Reserved. @@ -31,8 +31,8 @@ /* * listen.c -- * - * Listen on sockets and register callbacks for incoming - * connections. + * Listen on sockets and register callbacks for incoming + * connections. */ #include "nsd.h" @@ -50,7 +50,7 @@ typedef struct ListenData { * Local functions defined in this file */ -static Ns_SockProc ListenCallback; +static Ns_SockProc ListenCallback; /* * Static variables defined in this file @@ -71,7 +71,7 @@ static Ns_Mutex lock; /* Lock around portsTable. */ * None. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ @@ -90,100 +90,133 @@ NsInitListen(void) * * Ns_SockListenCallback -- * - * Listen on an address/port and register a callback to be run - * when connections come in on it. + * Listen on an address/port and register a callback to be run + * when connections come in on it. * * Results: - * NS_OK/NS_ERROR + * A valid NS_SOCK or NS_INVALID_SOCKET * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ -Ns_ReturnCode -Ns_SockListenCallback(const char *addr, unsigned short port, Ns_SockProc *proc, void *arg) +NS_SOCKET +Ns_SockListenCallback(const char *addr, unsigned short port, Ns_SockProc *proc, bool bind, void *arg) { - Tcl_HashTable *tablePtr = NULL; - Tcl_HashEntry *hPtr; - NS_SOCKET sock; - int isNew; - Ns_ReturnCode status; + NS_SOCKET sock = NS_INVALID_SOCKET; struct NS_SOCKADDR_STORAGE sa; struct sockaddr *saPtr = (struct sockaddr *)&sa; NS_NONNULL_ASSERT(proc != NULL); NS_NONNULL_ASSERT(arg != NULL); - if (Ns_GetSockAddr(saPtr, addr, port) != NS_OK) { - return NS_ERROR; - } + Ns_Log(Debug, "Ns_SockListenCallback: called with addr <%s> and port %hu", addr, port); - if (addr != NULL) { - /* - * Make sure we can bind to the specified interface. - */ - Ns_SockaddrSetPort(saPtr, 0u); - sock = Ns_SockBind(saPtr, NS_FALSE); - if (sock == NS_INVALID_SOCKET) { - return NS_ERROR; - } - ns_sockclose(sock); - } - status = NS_OK; + if (Ns_GetSockAddr(saPtr, addr, port) == NS_OK) { + NS_SOCKET bindsock; + Ns_ReturnCode status = NS_OK; - Ns_MutexLock(&lock); + //Ns_SockaddrSetPort(saPtr, 0u); + bindsock = Ns_SockBind(saPtr, NS_FALSE); - /* - * Update the global hash table that keeps track of which ports - * we're listening on. - */ - - hPtr = Tcl_CreateHashEntry(&portsTable, INT2PTR(port), &isNew); - if (isNew == 0) { - tablePtr = Tcl_GetHashValue(hPtr); - } else { - - sock = Ns_SockListen(NULL, port); - if (sock == NS_INVALID_SOCKET) { - Tcl_DeleteHashEntry(hPtr); - status = NS_ERROR; - } else { - status = Ns_SockSetNonBlocking(sock); - } - if (status == NS_OK) { - tablePtr = ns_malloc(sizeof(Tcl_HashTable)); - Tcl_InitHashTable(tablePtr, TCL_STRING_KEYS); - Tcl_SetHashValue(hPtr, tablePtr); - status = Ns_SockCallback(sock, ListenCallback, tablePtr, - (unsigned int)NS_SOCK_READ | (unsigned int)NS_SOCK_EXIT); + if (port == 0u) { + /* + * The specified port might be 0 to allocate a fresh, unused + * port for listening. Ns_SockBind() will update the port in + * the saPtr, update the local variable here to have a valid + * entry for the hash table below. + */ + port = Ns_SockaddrGetPort(saPtr); + Ns_Log(Debug, "Ns_SockListenCallback: Ns_GetSockAddr obtained port %hu", port); } - } - if (status == NS_OK) { - char ipString[NS_IPADDR_SIZE] = {'\0'}; - - assert(tablePtr != NULL); - - hPtr = Tcl_CreateHashEntry(tablePtr, - ns_inet_ntop(saPtr, ipString, NS_IPADDR_SIZE), - &isNew); - - if (isNew == 0) { - Ns_Log(Error, "listen callback: there is already a listen callback registered"); - status = NS_ERROR; - } else { - ListenData *ldPtr; - ldPtr = ns_malloc(sizeof(ListenData)); - ldPtr->proc = proc; - ldPtr->arg = arg; - Tcl_SetHashValue(hPtr, ldPtr); + if (!bind) { + /* + * Just make sure, we can bind to the specified interface. + */ + if (likely(bindsock != NS_INVALID_SOCKET)) { + ns_sockclose(bindsock); + bindsock = NS_INVALID_SOCKET; + } else { + sock = NS_INVALID_SOCKET; + status = NS_ERROR; + } + } + + if (status == NS_OK) { + Tcl_HashTable *tablePtr = NULL; + Tcl_HashEntry *hPtr; + int isNew; + + Ns_Log(Debug, "Ns_SockListenCallback: registering port %hu", port); + + Ns_MutexLock(&lock); + + /* + * Update the global hash table that keeps track of which ports + * we're listening on. + */ + hPtr = Tcl_CreateHashEntry(&portsTable, INT2PTR(port), &isNew); + if (isNew == 0) { + tablePtr = Tcl_GetHashValue(hPtr); + } else { + + if (bindsock != NS_INVALID_SOCKET) { + listen(bindsock, 5); + sock = bindsock; + } else { + sock = Ns_SockListen(NULL, port); + } + + if (sock == NS_INVALID_SOCKET) { + Tcl_DeleteHashEntry(hPtr); + status = NS_ERROR; + } else { + status = Ns_SockSetNonBlocking(sock); + } + if (status == NS_OK) { + tablePtr = ns_malloc(sizeof(Tcl_HashTable)); + Tcl_InitHashTable(tablePtr, TCL_STRING_KEYS); + Tcl_SetHashValue(hPtr, tablePtr); + status = Ns_SockCallback(sock, ListenCallback, tablePtr, + (unsigned int)NS_SOCK_READ | (unsigned int)NS_SOCK_EXIT); + } + if (status != NS_OK && sock != NS_INVALID_SOCKET) { + ns_sockclose(sock); + sock = NS_INVALID_SOCKET; + } + } + if (sock != NS_INVALID_SOCKET) { + char ipString[NS_IPADDR_SIZE] = {'\0'}; + + assert(tablePtr != NULL); + + hPtr = Tcl_CreateHashEntry(tablePtr, + ns_inet_ntop(saPtr, ipString, NS_IPADDR_SIZE), + &isNew); + Ns_Log(Debug, "Ns_SockListenCallback: registering IP addr %s isNew %d", ipString, isNew); + Ns_LogSockaddr(Debug, "... register IP + PROTO", (const struct sockaddr *) saPtr); + + if (isNew == 0) { + Ns_Log(Error, "listen callback: there is already a listen callback registered"); + ns_sockclose(sock); + sock = NS_INVALID_SOCKET; + } else { + ListenData *ldPtr; + + ldPtr = ns_malloc(sizeof(ListenData)); + ldPtr->proc = proc; + ldPtr->arg = arg; + Tcl_SetHashValue(hPtr, ldPtr); + } + } + Ns_MutexUnlock(&lock); } } - Ns_MutexUnlock(&lock); - - return status; + + return sock; } @@ -192,14 +225,14 @@ Ns_SockListenCallback(const char *addr, unsigned short port, Ns_SockProc *proc, * * Ns_SockPortBound -- * - * Determine if we're already listening on a given port on any - * address. + * Determine if we're already listening on a given port on any + * address. * * Results: * Boolean * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ @@ -221,14 +254,14 @@ Ns_SockPortBound(unsigned short port) * * ListenCallback -- * - * This is a wrapper callback that runs the user's callback iff - * a valid socket exists. + * This is a wrapper callback that runs the user's callback iff + * a valid socket exists. * * Results: - * NS_TRUE or NS_FALSE + * NS_TRUE or NS_FALSE * * Side effects: - * May close the socket if no user context can be found. + * May close the socket if no user context can be found. * *---------------------------------------------------------------------- */ @@ -250,7 +283,7 @@ ListenCallback(NS_SOCKET sock, void *arg, unsigned int why) } newSock = Ns_SockAccept(sock, NULL, NULL); - + if (likely(newSock != NS_INVALID_SOCKET)) { const Tcl_HashEntry *hPtr; const ListenData *ldPtr; @@ -266,9 +299,10 @@ ListenCallback(NS_SOCKET sock, void *arg, unsigned int why) return NS_FALSE; } ldPtr = NULL; - + (void)ns_inet_ntop((struct sockaddr *)&sa, ipString, NS_IPADDR_SIZE); - + Ns_Log(Debug, "ListenCallback: ipstring <%s>", ipString); + Ns_MutexLock(&lock); hPtr = Tcl_FindHashEntry(tablePtr, ipString); if (hPtr == NULL) { @@ -279,6 +313,8 @@ ListenCallback(NS_SOCKET sock, void *arg, unsigned int why) } Ns_MutexUnlock(&lock); + Ns_LogSockaddr(Notice, "... query IP + PROTO", (const struct sockaddr *) &sa); + if (ldPtr == NULL) { /* * There was no hash entry for the listen callback diff --git a/nsd/log.c b/nsd/log.c index c6c9fec0..9a5cfdca 100644 --- a/nsd/log.c +++ b/nsd/log.c @@ -256,11 +256,10 @@ NsInitLog(void) Ns_AddLogFilter(LogToFile, INT2PTR(STDERR_FILENO), NULL); /* - * Initialise the entire space with backwards-compatible integer keys. + * Initialize the entire space with backwards-compatible integer keys. */ - for (i = PredefinedLogSeveritiesCount; i < severityMaxCount; i++) { - snprintf(buf, sizeof(buf), "%d", (int)i); + (void) ns_uint32toa(buf, (uint32_t)i); hPtr = Tcl_CreateHashEntry(&severityTable, buf, &isNew); Tcl_SetHashValue(hPtr, INT2PTR(i)); severityConfig[i].label = Tcl_GetHashKey(&severityTable, hPtr); @@ -268,9 +267,8 @@ NsInitLog(void) } /* - * Initialise the built-in severities and lower-case aliases. + * Initialize the built-in severities and lower-case aliases. */ - for (i = 0; i < PredefinedLogSeveritiesCount; i++) { size_t labelLength; diff --git a/nsd/nsd.h b/nsd/nsd.h index 07998fde..bf2a7260 100644 --- a/nsd/nsd.h +++ b/nsd/nsd.h @@ -450,6 +450,7 @@ typedef struct Driver { Ns_DriverCloseProc *closeProc; Ns_DriverClientInitProc *clientInitProc; /* Optional - initialization of client connections */ + const char *defserver; /* default server, might be NULL */ long closewait; /* Graceful close timeout */ long keepwait; /* Keepalive timeout */ size_t keepmaxdownloadsize; /* When set, allow keepalive only for download requests up to this size */ @@ -491,6 +492,7 @@ typedef struct Driver { Tcl_WideInt errors; /* Dropped requests due to errors */ } stats; unsigned short port; /* Port in location */ + unsigned short defport; /* Default port */ bool reuseport; /* Allow optionally multiple drivers to connect to the same port */ } Driver; @@ -880,19 +882,20 @@ typedef struct NsServer { */ struct { - const char *library; - struct TclTrace *firstTracePtr; - struct TclTrace *lastTracePtr; - Tcl_Obj *initfile; - Ns_RWLock lock; - const char *script; - int length; - int epoch; - Tcl_Obj *modules; - Tcl_HashTable runTable; - const char **errorLogHeaders; - Tcl_HashTable caches; - Ns_Mutex cachelock; + const char *library; + struct TclTrace *firstTracePtr; + struct TclTrace *lastTracePtr; + Tcl_Obj *initfile; + Ns_RWLock lock; + const char *script; + int length; + int epoch; + Tcl_Obj *modules; + Tcl_HashTable runTable; + const char **errorLogHeaders; + Tcl_HashTable caches; + Ns_Mutex cachelock; + uintptr_t transactionEpoch; /* * The following tracks synchronization @@ -1059,7 +1062,8 @@ typedef struct NsInterp { */ Tcl_HashTable httpRequests; - bool deleteInterp; /* Interp should be deleted on next deallocation */ + Ns_CacheTransactionStack cacheTransactionStack; + bool deleteInterp; /* Interp should be deleted on next deallocation */ } NsInterp; @@ -1118,6 +1122,9 @@ NS_EXTERN Tcl_ObjCmdProc NsTclCacheLappendObjCmd, NsTclCacheNamesObjCmd, NsTclCacheStatsObjCmd, + NsTclCacheTransactionBeginObjCmd, + NsTclCacheTransactionCommitObjCmd, + NsTclCacheTransactionRollbackObjCmd, NsTclCancelObjCmd, NsTclChanObjCmd, NsTclCharsetsObjCmd, @@ -1353,6 +1360,11 @@ NS_EXTERN int NSDriverClientOpen(Tcl_Interp *interp, const char *driverName, const Ns_Time *timeoutPtr, Sock **sockPtrPtr) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(3) NS_GNUC_NONNULL(4) NS_GNUC_NONNULL(5) NS_GNUC_NONNULL(6) NS_GNUC_NONNULL(7); +NS_EXTERN int NSDriverSockNew(Tcl_Interp *interp, NS_SOCKET sock, + const char *protocol, const char *driverName, const char *methodName, + Sock **sockPtrPtr) + NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(3) NS_GNUC_NONNULL(5) NS_GNUC_NONNULL(6); + NS_EXTERN ssize_t NsSockSendFileBufsIndirect(Ns_Sock *sock, const Ns_FileVec *bufs, int nbufs, const Ns_Time *timeoutPtr, unsigned int flags, Ns_DriverSendProc *sendProc) @@ -1476,6 +1488,7 @@ NS_EXTERN void NsRestoreSignals(void); NS_EXTERN void NsSendSignal(int sig); NS_EXTERN Tcl_Obj * NsDriverStats(Tcl_Interp *interp) NS_GNUC_NONNULL(1); +NS_EXTERN void NsDriverMapVirtualServers(void); /* * limits.c diff --git a/nsd/nsmain.c b/nsd/nsmain.c index 6dbc5f0b..d3f40090 100644 --- a/nsd/nsmain.c +++ b/nsd/nsmain.c @@ -779,6 +779,7 @@ Ns_Main(int argc, char *const* argv, Ns_ServerInitProc *initProc) for (i = 0u; i < Ns_SetSize(servers); ++i) { server = Ns_SetKey(servers, i); NsInitServer(server, initProc); + } } nsconf.defaultServer = server; @@ -790,10 +791,23 @@ Ns_Main(int argc, char *const* argv, Ns_ServerInitProc *initProc) NsInitStaticModules(NULL); /* - * Run pre-startups and start the servers. + * Run pre-startup procs */ NsRunPreStartupProcs(); + + /* + * Map virtual servers. This requires that all servers and all drivers are + * initialized (can be found via global data structures). Drivers are + * created via NsRunPreStartupProcs(). + */ + + NsDriverMapVirtualServers(); + + /* + * Start the servers and drivers. + */ + NsStartServers(); NsStartDrivers(); diff --git a/nsd/op.c b/nsd/op.c index e6e69dbe..69fe86e9 100644 --- a/nsd/op.c +++ b/nsd/op.c @@ -580,11 +580,11 @@ NsGetRequestProcs(Tcl_DString *dsPtr, const char *server) NS_NONNULL_ASSERT(server != NULL); servPtr = NsGetServer(server); - assert(servPtr != NULL); - - Ns_MutexLock(&ulock); - Ns_UrlSpecificWalk(uid, servPtr->server, WalkCallback, dsPtr); - Ns_MutexUnlock(&ulock); + if (likely(servPtr != NULL)) { + Ns_MutexLock(&ulock); + Ns_UrlSpecificWalk(uid, servPtr->server, WalkCallback, dsPtr); + Ns_MutexUnlock(&ulock); + } } static void diff --git a/nsd/pathname.c b/nsd/pathname.c index 536f4b35..3499c4a9 100644 --- a/nsd/pathname.c +++ b/nsd/pathname.c @@ -84,40 +84,51 @@ NsConfigVhost(void) static Ns_ReturnCode ConfigServerVhost(const char *server) { - NsServer *servPtr = NsGetServer(server); - Ns_DString ds; - const char *path; + NsServer *servPtr; + Ns_ReturnCode result; NS_NONNULL_ASSERT(server != NULL); - assert(servPtr->fastpath.pagedir != NULL); + + servPtr = NsGetServer(server); + if (unlikely(servPtr == NULL)) { + Ns_Log(Warning, "Could configure vhost; server '%s' unknown", server); + result = NS_ERROR; + + } else { + Ns_DString ds; + const char *path; + + assert(servPtr->fastpath.pagedir != NULL); - path = Ns_ConfigGetPath(server, NULL, "vhost", (char *)0); - - servPtr->vhost.enabled = Ns_ConfigBool(path, "enabled", NS_FALSE); - if (servPtr->vhost.enabled - && Ns_PathIsAbsolute(servPtr->fastpath.pagedir) == NS_TRUE) { - Ns_Log(Error, "vhost[%s]: disabled, pagedir not relative: %s", - server, servPtr->fastpath.pagedir); - servPtr->vhost.enabled = NS_FALSE; - } - if (Ns_ConfigBool(path, "stripwww", NS_TRUE)) { - servPtr->vhost.opts |= NSD_STRIP_WWW; - } - if (Ns_ConfigBool(path, "stripport", NS_TRUE)) { - servPtr->vhost.opts |= NSD_STRIP_PORT; - } - servPtr->vhost.hostprefix = Ns_ConfigGetValue(path, "hostprefix"); - servPtr->vhost.hosthashlevel = - Ns_ConfigIntRange(path, "hosthashlevel", 0, 0, 5); - - if (servPtr->vhost.enabled) { - Ns_DStringInit(&ds); - (void) NsPageRoot(&ds, servPtr, "www.example.com:80"); - Ns_Log(Notice, "vhost[%s]: www.example.com:80 -> %s",server,ds.string); - Ns_DStringFree(&ds); - } + path = Ns_ConfigGetPath(server, NULL, "vhost", (char *)0); + + servPtr->vhost.enabled = Ns_ConfigBool(path, "enabled", NS_FALSE); + if (servPtr->vhost.enabled + && Ns_PathIsAbsolute(servPtr->fastpath.pagedir) == NS_TRUE) { + Ns_Log(Error, "vhost[%s]: disabled, pagedir not relative: %s", + server, servPtr->fastpath.pagedir); + servPtr->vhost.enabled = NS_FALSE; + } + if (Ns_ConfigBool(path, "stripwww", NS_TRUE)) { + servPtr->vhost.opts |= NSD_STRIP_WWW; + } + if (Ns_ConfigBool(path, "stripport", NS_TRUE)) { + servPtr->vhost.opts |= NSD_STRIP_PORT; + } + servPtr->vhost.hostprefix = Ns_ConfigGetValue(path, "hostprefix"); + servPtr->vhost.hosthashlevel = + Ns_ConfigIntRange(path, "hosthashlevel", 0, 0, 5); - return NS_OK; + if (servPtr->vhost.enabled) { + Ns_DStringInit(&ds); + (void) NsPageRoot(&ds, servPtr, "www.example.com:80"); + Ns_Log(Notice, "vhost[%s]: www.example.com:80 -> %s",server,ds.string); + Ns_DStringFree(&ds); + } + result = NS_OK; + } + + return result; } diff --git a/nsd/queue.c b/nsd/queue.c index 80385448..ace12415 100644 --- a/nsd/queue.c +++ b/nsd/queue.c @@ -40,21 +40,21 @@ * Local functions defined in this file */ -static void ConnRun(const ConnThreadArg *argPtr, Conn *connPtr) - NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2); +static void ConnRun(Conn *connPtr) + NS_GNUC_NONNULL(1); static void CreateConnThread(ConnPool *poolPtr) NS_GNUC_NONNULL(1); -static void AppendConn(Tcl_DString *dsPtr, const Conn *connPtr, const char *state) +static void AppendConn(Tcl_DString *dsPtr, const Conn *connPtr, const char *state, bool checkforproxy) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(3); -static void AppendConnList(Tcl_DString *dsPtr, const Conn *firstPtr, const char *state) +static void AppendConnList(Tcl_DString *dsPtr, const Conn *firstPtr, const char *state, bool checkforproxy) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(3); -static bool neededAdditionalConnectionThreads(const ConnPool *poolPtr) +static bool neededAdditionalConnectionThreads(const ConnPool *poolPtr) NS_GNUC_NONNULL(1); -static void WakeupConnThreads(ConnPool *poolPtr) +static void WakeupConnThreads(ConnPool *poolPtr) NS_GNUC_NONNULL(1); static Ns_ReturnCode MapspecParse(Tcl_Interp *interp, Tcl_Obj *mapspecObj, char **method, char **url) @@ -80,6 +80,17 @@ static int ServerUnmapObjCmd(const ClientData clientData, Tcl_Interp *interp, in NsServer *servPtr, int nargs) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(4) NS_GNUC_NONNULL(5); +static void ConnThreadSetName(const char *server, const char *pool, uintptr_t threadId, uintptr_t connId) + NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2); + +static int ServerListActive(Tcl_DString *dsPtr, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv, + ConnPool *poolPtr, int nargs) + NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(4) NS_GNUC_NONNULL(5); + +static int ServerListQueued(Tcl_DString *dsPtr, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv, + ConnPool *poolPtr, int nargs) + NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(4) NS_GNUC_NONNULL(5); + static Ns_ArgProc WalkCallback; /* @@ -97,11 +108,11 @@ static int poolid = 0; #if 0 static void ConnThreadQueuePrint(ConnPool *poolPtr, char *key) { ConnThreadArg *aPtr; - + fprintf(stderr, "%s: thread queue (idle %d): ", key, poolPtr->threads.idle); Ns_MutexLock(&poolPtr->tqueue.lock); for (aPtr = poolPtr->tqueue.nextPtr; aPtr; aPtr = aPtr->nextPtr) { - fprintf(stderr, "[%d] state %d, ", ThreadNr(poolPtr, aPtr), aPtr->state); + fprintf(stderr, "[%d] state %d, ", ThreadNr(poolPtr, aPtr), aPtr->state); } Ns_MutexUnlock(&poolPtr->tqueue.lock); fprintf(stderr, "\n"); @@ -189,8 +200,8 @@ NsMapPool(ConnPool *poolPtr, const char *map, unsigned int flags) if (Tcl_SplitList(NULL, map, &mc, &mv) == TCL_OK) { if (mc == 2) { Ns_UrlSpecificSet(server, mv[0], mv[1], poolid, poolPtr, flags, NULL); - Ns_Log(Notice, "pool[%s]: mapped %s %s -> %s", - server, mv[0], mv[1], poolPtr->pool); + Ns_Log(Notice, "pool[%s]: mapped %s %s -> %s", + server, mv[0], mv[1], poolPtr->pool); } Tcl_Free((char *) mv); } @@ -215,15 +226,15 @@ NsMapPool(ConnPool *poolPtr, const char *map, unsigned int flags) * *---------------------------------------------------------------------- */ -static bool +static bool neededAdditionalConnectionThreads(const ConnPool *poolPtr) { bool wantCreate; NS_NONNULL_ASSERT(poolPtr != NULL); - /* + /* * Create new connection threads, if - * + * * - there is currently no connection thread being created, or * parallel creates are allowed and there are more than * highwatermark requests queued, @@ -234,39 +245,39 @@ neededAdditionalConnectionThreads(const ConnPool *poolPtr) { * - AND there are not yet max-threads running. * */ - if ( (poolPtr->threads.creating == 0 - || poolPtr->wqueue.wait.num > poolPtr->wqueue.highwatermark - ) - && (poolPtr->threads.current < poolPtr->threads.min - || (poolPtr->wqueue.wait.num > poolPtr->wqueue.lowwatermark) - ) - && poolPtr->threads.current < poolPtr->threads.max - ) { - - Ns_MutexLock(&poolPtr->servPtr->pools.lock); - wantCreate = (!poolPtr->servPtr->pools.shutdown); - Ns_MutexUnlock(&poolPtr->servPtr->pools.lock); - - /*Ns_Log(Notice, "[%s] wantCreate %d (creating %d current %d idle %d waiting %d)", - poolPtr->servPtr->server, - wantCreate, - poolPtr->threads.creating, - poolPtr->threads.current, - poolPtr->threads.idle, - poolPtr->wqueue.wait.num - );*/ + if ( (poolPtr->threads.creating == 0 + || poolPtr->wqueue.wait.num > poolPtr->wqueue.highwatermark + ) + && (poolPtr->threads.current < poolPtr->threads.min + || (poolPtr->wqueue.wait.num > poolPtr->wqueue.lowwatermark) + ) + && poolPtr->threads.current < poolPtr->threads.max + ) { + + Ns_MutexLock(&poolPtr->servPtr->pools.lock); + wantCreate = (!poolPtr->servPtr->pools.shutdown); + Ns_MutexUnlock(&poolPtr->servPtr->pools.lock); + + /*Ns_Log(Notice, "[%s] wantCreate %d (creating %d current %d idle %d waiting %d)", + poolPtr->servPtr->server, + wantCreate, + poolPtr->threads.creating, + poolPtr->threads.current, + poolPtr->threads.idle, + poolPtr->wqueue.wait.num + );*/ } else { wantCreate = NS_FALSE; - + /*Ns_Log(Notice, "[%s] do not wantCreate creating %d, idle %d < min %d, current %d < max %d, waiting %d)", - poolPtr->servPtr->server, - poolPtr->threads.creating, - poolPtr->threads.idle, - poolPtr->threads.min, - poolPtr->threads.current, - poolPtr->threads.max, - poolPtr->wqueue.wait.num);*/ - + poolPtr->servPtr->server, + poolPtr->threads.creating, + poolPtr->threads.idle, + poolPtr->threads.min, + poolPtr->threads.current, + poolPtr->threads.max, + poolPtr->wqueue.wait.num);*/ + } return wantCreate; @@ -300,10 +311,10 @@ NsEnsureRunningConnectionThreads(const NsServer *servPtr, ConnPool *poolPtr) { NS_NONNULL_ASSERT(servPtr != NULL); if (poolPtr == NULL) { - /* - * Use the default pool for the time being, if no pool was - * provided - */ + /* + * Use the default pool for the time being, if no pool was + * provided + */ poolPtr = servPtr->pools.defaultPtr; } @@ -312,19 +323,19 @@ NsEnsureRunningConnectionThreads(const NsServer *servPtr, ConnPool *poolPtr) { create = neededAdditionalConnectionThreads(poolPtr); if (create) { - poolPtr->threads.current ++; - poolPtr->threads.creating ++; + poolPtr->threads.current ++; + poolPtr->threads.creating ++; } Ns_MutexUnlock(&poolPtr->threads.lock); Ns_MutexUnlock(&poolPtr->wqueue.lock); if (create) { - Ns_Log(Notice, "NsEnsureRunningConnectionThreads wantCreate %d waiting %d idle %d current %d", - (int)create, - poolPtr->wqueue.wait.num, - poolPtr->threads.idle, - poolPtr->threads.current); + Ns_Log(Notice, "NsEnsureRunningConnectionThreads wantCreate %d waiting %d idle %d current %d", + (int)create, + poolPtr->wqueue.wait.num, + poolPtr->threads.idle, + poolPtr->threads.current); CreateConnThread(poolPtr); } } @@ -358,7 +369,7 @@ NsQueueConn(Sock *sockPtr, const Ns_Time *nowPtr) NS_NONNULL_ASSERT(sockPtr != NULL); NS_NONNULL_ASSERT(nowPtr != NULL); assert(sockPtr->drvPtr != NULL); - + sockPtr->drvPtr->stats.received++; servPtr = sockPtr->servPtr; @@ -381,29 +392,29 @@ NsQueueConn(Sock *sockPtr, const Ns_Time *nowPtr) * Queue connection if possible (e.g. no shutdown, a free Conn is * available, ...) */ - + if (!servPtr->pools.shutdown) { - Ns_MutexLock(&poolPtr->wqueue.lock); - if (poolPtr->wqueue.freePtr != NULL) { - connPtr = poolPtr->wqueue.freePtr; - poolPtr->wqueue.freePtr = connPtr->nextPtr; + Ns_MutexLock(&poolPtr->wqueue.lock); + if (poolPtr->wqueue.freePtr != NULL) { + connPtr = poolPtr->wqueue.freePtr; + poolPtr->wqueue.freePtr = connPtr->nextPtr; connPtr->nextPtr = NULL; - } - Ns_MutexUnlock(&poolPtr->wqueue.lock); + } + Ns_MutexUnlock(&poolPtr->wqueue.lock); if (connPtr != NULL) { - /* - * We have got a free connPtr from the pool. Initialize the - * connPtr and copy flags from the socket. - */ - - /* ConnThreadQueuePrint(poolPtr, "driver");*/ - - Ns_MutexLock(&servPtr->pools.lock); + /* + * We have got a free connPtr from the pool. Initialize the + * connPtr and copy flags from the socket. + */ + + /* ConnThreadQueuePrint(poolPtr, "driver");*/ + + Ns_MutexLock(&servPtr->pools.lock); connPtr->id = servPtr->pools.nextconnid++; - poolPtr->stats.processed++; - Ns_MutexUnlock(&servPtr->pools.lock); + poolPtr->stats.processed++; + Ns_MutexUnlock(&servPtr->pools.lock); connPtr->requestQueueTime = *nowPtr; connPtr->sockPtr = sockPtr; @@ -411,124 +422,124 @@ NsQueueConn(Sock *sockPtr, const Ns_Time *nowPtr) connPtr->poolPtr = poolPtr; connPtr->server = servPtr->server; connPtr->location = sockPtr->location; - connPtr->flags = sockPtr->flags; - if ((sockPtr->drvPtr->opts & NS_DRIVER_ASYNC) == 0u) { - connPtr->acceptTime = *nowPtr; - } else { - connPtr->acceptTime = sockPtr->acceptTime; - } - sockPtr->acceptTime.sec = 0; /* invalidate time */ - - /* - * Try to get an entry from the connection thread queue, - * and dequeue it when possible. - */ - if (poolPtr->tqueue.nextPtr != NULL) { - Ns_MutexLock(&poolPtr->tqueue.lock); - if (poolPtr->tqueue.nextPtr != NULL) { - argPtr = poolPtr->tqueue.nextPtr; - poolPtr->tqueue.nextPtr = argPtr->nextPtr; - } - Ns_MutexUnlock(&poolPtr->tqueue.lock); - } - - if (argPtr != NULL) { - /* - * We could obtain an idle thread. Dequeue the entry, - * such that no one else might grab it, and fill in the - * connPtr that should be run by this thread. - */ - - assert(argPtr->state == connThread_idle); - argPtr->connPtr = connPtr; - - Ns_MutexLock(&poolPtr->wqueue.lock); - Ns_MutexLock(&poolPtr->threads.lock); - create = neededAdditionalConnectionThreads(poolPtr); - Ns_MutexUnlock(&poolPtr->threads.lock); - Ns_MutexUnlock(&poolPtr->wqueue.lock); - - } else { - /* - * There is no connection thread ready, so we add the - * connection to the waiting queue. - */ - Ns_MutexLock(&poolPtr->wqueue.lock); - if (poolPtr->wqueue.wait.firstPtr == NULL) { - poolPtr->wqueue.wait.firstPtr = connPtr; - } else { - poolPtr->wqueue.wait.lastPtr->nextPtr = connPtr; - } - poolPtr->wqueue.wait.lastPtr = connPtr; - poolPtr->wqueue.wait.num ++; - Ns_MutexLock(&poolPtr->threads.lock); - poolPtr->stats.queued++; - create = neededAdditionalConnectionThreads(poolPtr); - Ns_MutexUnlock(&poolPtr->threads.lock); - Ns_MutexUnlock(&poolPtr->wqueue.lock); - } + connPtr->flags = sockPtr->flags; + if ((sockPtr->drvPtr->opts & NS_DRIVER_ASYNC) == 0u) { + connPtr->acceptTime = *nowPtr; + } else { + connPtr->acceptTime = sockPtr->acceptTime; + } + sockPtr->acceptTime.sec = 0; /* invalidate time */ + + /* + * Try to get an entry from the connection thread queue, + * and dequeue it when possible. + */ + if (poolPtr->tqueue.nextPtr != NULL) { + Ns_MutexLock(&poolPtr->tqueue.lock); + if (poolPtr->tqueue.nextPtr != NULL) { + argPtr = poolPtr->tqueue.nextPtr; + poolPtr->tqueue.nextPtr = argPtr->nextPtr; + } + Ns_MutexUnlock(&poolPtr->tqueue.lock); + } + + if (argPtr != NULL) { + /* + * We could obtain an idle thread. Dequeue the entry, + * such that no one else might grab it, and fill in the + * connPtr that should be run by this thread. + */ + + assert(argPtr->state == connThread_idle); + argPtr->connPtr = connPtr; + + Ns_MutexLock(&poolPtr->wqueue.lock); + Ns_MutexLock(&poolPtr->threads.lock); + create = neededAdditionalConnectionThreads(poolPtr); + Ns_MutexUnlock(&poolPtr->threads.lock); + Ns_MutexUnlock(&poolPtr->wqueue.lock); + + } else { + /* + * There is no connection thread ready, so we add the + * connection to the waiting queue. + */ + Ns_MutexLock(&poolPtr->wqueue.lock); + if (poolPtr->wqueue.wait.firstPtr == NULL) { + poolPtr->wqueue.wait.firstPtr = connPtr; + } else { + poolPtr->wqueue.wait.lastPtr->nextPtr = connPtr; + } + poolPtr->wqueue.wait.lastPtr = connPtr; + poolPtr->wqueue.wait.num ++; + Ns_MutexLock(&poolPtr->threads.lock); + poolPtr->stats.queued++; + create = neededAdditionalConnectionThreads(poolPtr); + Ns_MutexUnlock(&poolPtr->threads.lock); + Ns_MutexUnlock(&poolPtr->wqueue.lock); + } } } if (connPtr == NULL) { - Ns_Log(Notice, "[%s pool %s] All available connections are used, waiting %d idle %d current %d", - poolPtr->servPtr->server, + Ns_Log(Notice, "[%s pool %s] All available connections are used, waiting %d idle %d current %d", + poolPtr->servPtr->server, poolPtr->pool, - poolPtr->wqueue.wait.num, - poolPtr->threads.idle, - poolPtr->threads.current); - queued = NS_FALSE; + poolPtr->wqueue.wait.num, + poolPtr->threads.idle, + poolPtr->threads.current); + queued = NS_FALSE; create = NS_FALSE; } else if (argPtr != NULL) { - /* - * We have a connection thread ready. - * - * Perform lock just at the "right" debug level. - */ - if (Ns_LogSeverityEnabled(Debug)) { - int idle; - - Ns_MutexLock(&poolPtr->threads.lock); - idle = poolPtr->threads.idle; - Ns_MutexUnlock(&poolPtr->threads.lock); - - Ns_Log(Debug, "[%ld] dequeue thread connPtr %p idle %d state %d create %d", - ThreadNr(poolPtr, argPtr), (void *)connPtr, idle, argPtr->state, (int)create); - } - - /* - * Signal the associated thread to start with this request. - */ - Ns_MutexLock(&argPtr->lock); + /* + * We have a connection thread ready. + * + * Perform lock just at the "right" debug level. + */ + if (Ns_LogSeverityEnabled(Debug)) { + int idle; + + Ns_MutexLock(&poolPtr->threads.lock); + idle = poolPtr->threads.idle; + Ns_MutexUnlock(&poolPtr->threads.lock); + + Ns_Log(Debug, "[%ld] dequeue thread connPtr %p idle %d state %d create %d", + ThreadNr(poolPtr, argPtr), (void *)connPtr, idle, argPtr->state, (int)create); + } + + /* + * Signal the associated thread to start with this request. + */ + Ns_MutexLock(&argPtr->lock); Ns_CondSignal(&argPtr->cond); - Ns_MutexUnlock(&argPtr->lock); + Ns_MutexUnlock(&argPtr->lock); } else { - if (Ns_LogSeverityEnabled(Debug)) { - Ns_Log(Debug, "add waiting connPtr %p => waiting %d create %d", - (void *)connPtr, poolPtr->wqueue.wait.num, (int)create); - } + if (Ns_LogSeverityEnabled(Debug)) { + Ns_Log(Debug, "add waiting connPtr %p => waiting %d create %d", + (void *)connPtr, poolPtr->wqueue.wait.num, (int)create); + } } if (create) { - int idle, current; + int idle, current; - Ns_MutexLock(&poolPtr->threads.lock); - idle = poolPtr->threads.idle; - current = poolPtr->threads.current; - poolPtr->threads.current ++; - poolPtr->threads.creating ++; + Ns_MutexLock(&poolPtr->threads.lock); + idle = poolPtr->threads.idle; + current = poolPtr->threads.current; + poolPtr->threads.current ++; + poolPtr->threads.creating ++; Ns_MutexUnlock(&poolPtr->threads.lock); - Ns_Log(Notice, "NsQueueConn wantCreate %d waiting %d idle %d current %d", - (int)create, - poolPtr->wqueue.wait.num, - idle, - current); + Ns_Log(Notice, "NsQueueConn wantCreate %d waiting %d idle %d current %d", + (int)create, + poolPtr->wqueue.wait.num, + idle, + current); - CreateConnThread(poolPtr); - } + CreateConnThread(poolPtr); + } return queued; } @@ -566,7 +577,7 @@ WalkCallback(Ns_DString *dsPtr, const void *arg) * Implements the "ns_server ... maxthreads ..." command. * * Results: - * Tcl result. + * Tcl result. * * Side effects: * Might update maxthreads setting of a pool @@ -586,7 +597,7 @@ ServerMaxThreadsObjCmd(const ClientData UNUSED(clientData), Tcl_Interp *interp, NS_NONNULL_ASSERT(interp != NULL); NS_NONNULL_ASSERT(objv != NULL); NS_NONNULL_ASSERT(poolPtr != NULL); - + if (Ns_ParseObjv(NULL, args, interp, objc-nargs, objc, objv) != NS_OK) { result = TCL_ERROR; } else if (nargs == 1) { @@ -599,12 +610,12 @@ ServerMaxThreadsObjCmd(const ClientData UNUSED(clientData), Tcl_Interp *interp, Ns_MutexUnlock(&poolPtr->threads.lock); } } else { - /* + /* * Called without an argument, just return the current setting. */ assert(nargs == 0); } - + if (result == TCL_OK) { Tcl_SetObjResult(interp, Tcl_NewIntObj(poolPtr->threads.max)); } @@ -620,7 +631,7 @@ ServerMaxThreadsObjCmd(const ClientData UNUSED(clientData), Tcl_Interp *interp, * Implements the "ns_server ... minthreads ..." command. * * Results: - * Tcl result. + * Tcl result. * * Side effects: * Might update minthreads setting of a pool @@ -640,7 +651,7 @@ ServerMinThreadsObjCmd(const ClientData UNUSED(clientData), Tcl_Interp *interp, NS_NONNULL_ASSERT(interp != NULL); NS_NONNULL_ASSERT(objv != NULL); NS_NONNULL_ASSERT(poolPtr != NULL); - + if (Ns_ParseObjv(NULL, args, interp, objc-nargs, objc, objv) != NS_OK) { result = TCL_ERROR; } else if (nargs == 1) { @@ -653,12 +664,12 @@ ServerMinThreadsObjCmd(const ClientData UNUSED(clientData), Tcl_Interp *interp, Ns_MutexUnlock(&poolPtr->threads.lock); } } else { - /* + /* * Called without an argument, just return the current setting. */ assert(nargs == 0); } - + if (result == TCL_OK) { Tcl_SetObjResult(interp, Tcl_NewIntObj(poolPtr->threads.min)); } @@ -694,7 +705,7 @@ MapspecParse( Tcl_Interp *interp, Tcl_Obj *mapspecObj, char **method, char **url NS_NONNULL_ASSERT(mapspecObj != NULL); NS_NONNULL_ASSERT(method != NULL); NS_NONNULL_ASSERT(url != NULL); - + if (Tcl_ListObjGetElements(NULL, mapspecObj, &oc, &ov) == TCL_OK && oc == 2) { *method = Tcl_GetString(ov[0]); *url = Tcl_GetString(ov[1]); @@ -705,7 +716,7 @@ MapspecParse( Tcl_Interp *interp, Tcl_Obj *mapspecObj, char **method, char **url Tcl_GetString(mapspecObj)); status = NS_ERROR; } - + return status; } @@ -718,7 +729,7 @@ MapspecParse( Tcl_Interp *interp, Tcl_Obj *mapspecObj, char **method, char **url * Implements the "ns_server ... map ..." command. * * Results: - * Tcl result. + * Tcl result. * * Side effects: * Map method + URL to specified pool @@ -734,7 +745,7 @@ ServerMapObjCmd(const ClientData UNUSED(clientData), Tcl_Interp *interp, int obj Ns_ObjvSpec lopts[] = { {"-noinherit", Ns_ObjvBool, &noinherit, INT2PTR(NS_TRUE)}, {NULL, NULL, NULL, NULL} - }; + }; Ns_ObjvSpec args[] = { {"?mapspec", Ns_ObjvObj, &mapspecObj, NULL}, {NULL, NULL, NULL, NULL} @@ -744,26 +755,26 @@ ServerMapObjCmd(const ClientData UNUSED(clientData), Tcl_Interp *interp, int obj NS_NONNULL_ASSERT(objv != NULL); NS_NONNULL_ASSERT(servPtr != NULL); NS_NONNULL_ASSERT(poolPtr != NULL); - + if (Ns_ParseObjv(lopts, args, interp, objc-nargs, objc, objv) != NS_OK) { result = TCL_ERROR; } else if (mapspecObj != NULL) { char *method, *url; - + if (MapspecParse(interp, mapspecObj, &method, &url) != NS_OK) { result = TCL_ERROR; } else { unsigned int flags = 0u; - + if (noinherit != 0) { flags |= NS_OP_NOINHERIT; } - + Ns_MutexLock(&servPtr->urlspace.lock); Ns_UrlSpecificSet(servPtr->server, method, url, poolid, poolPtr, flags, NULL); Ns_MutexUnlock(&servPtr->urlspace.lock); - - Ns_Log(Notice, "pool[%s]: mapped %s %s -> %s", + + Ns_Log(Notice, "pool[%s]: mapped %s %s -> %s", servPtr->server, method, url, poolPtr->pool); } @@ -771,24 +782,24 @@ ServerMapObjCmd(const ClientData UNUSED(clientData), Tcl_Interp *interp, int obj Tcl_DString ds, *dsPtr = &ds; Tcl_Obj **ov, *fullListObj; int oc; - + /* * Return the current mappings just in the case, when the map * operation was called without the optional argument. */ Ns_DStringInit(dsPtr); - + Ns_MutexLock(&servPtr->urlspace.lock); Ns_UrlSpecificWalk(poolid, servPtr->server, WalkCallback, dsPtr); Ns_MutexUnlock(&servPtr->urlspace.lock); - + /* * Convert the Tcl_Dstring into a list, and filter the elements * from different pools. */ fullListObj = Tcl_NewStringObj(dsPtr->string, dsPtr->length); Tcl_IncrRefCount(fullListObj); - + result = Tcl_ListObjGetElements(interp, fullListObj, &oc, &ov); if (result == TCL_OK) { Tcl_Obj *resultObj; @@ -799,7 +810,7 @@ ServerMapObjCmd(const ClientData UNUSED(clientData), Tcl_Interp *interp, int obj * error should not occur. */ resultObj = Tcl_NewListObj(0, NULL); - + for (i = 0; i < oc; i++) { Tcl_Obj *elemObj = ov[i]; int length; @@ -811,11 +822,11 @@ ServerMapObjCmd(const ClientData UNUSED(clientData), Tcl_Interp *interp, int obj result = Tcl_ListObjLength(interp, elemObj, &length); if (result == TCL_OK) { Tcl_Obj *lastSubElem; - + result = Tcl_ListObjIndex(interp, elemObj, length-1, &lastSubElem); if (result == TCL_OK) { const char *pool = Tcl_GetString(lastSubElem); - + if (!STREQ(poolPtr->pool, pool)) { continue; } @@ -847,7 +858,7 @@ ServerMapObjCmd(const ClientData UNUSED(clientData), Tcl_Interp *interp, int obj Tcl_DStringFree(dsPtr); } - + return result; } @@ -860,7 +871,7 @@ ServerMapObjCmd(const ClientData UNUSED(clientData), Tcl_Interp *interp, int obj * Implements the "ns_server ... mapped " command. * * Results: - * Tcl result. + * Tcl result. * * Side effects: * None @@ -878,7 +889,7 @@ ServerMappedObjCmd(const ClientData UNUSED(clientData), Tcl_Interp *interp, int {"-exact", Ns_ObjvBool, &exact, INT2PTR(NS_TRUE)}, {"-noinherit", Ns_ObjvBool, &noinherit, INT2PTR(NS_TRUE)}, {NULL, NULL, NULL, NULL} - }; + }; Ns_ObjvSpec args[] = { {"mapspec", Ns_ObjvObj, &mapspecObj, NULL}, {NULL, NULL, NULL, NULL} @@ -887,7 +898,7 @@ ServerMappedObjCmd(const ClientData UNUSED(clientData), Tcl_Interp *interp, int NS_NONNULL_ASSERT(interp != NULL); NS_NONNULL_ASSERT(objv != NULL); NS_NONNULL_ASSERT(servPtr != NULL); - + if (Ns_ParseObjv(lopts, args, interp, objc-nargs, objc, objv) != NS_OK) { result = TCL_ERROR; } else if (MapspecParse(interp, mapspecObj, &method, &url) != NS_OK) { @@ -896,7 +907,7 @@ ServerMappedObjCmd(const ClientData UNUSED(clientData), Tcl_Interp *interp, int unsigned int flags = 0u; const ConnPool *mappedPoolPtr; NsUrlSpaceOp op; - + if (noinherit != 0) { flags |= NS_OP_NOINHERIT; } @@ -915,7 +926,7 @@ ServerMappedObjCmd(const ClientData UNUSED(clientData), Tcl_Interp *interp, int Tcl_SetObjResult(interp, Tcl_NewStringObj(mappedPoolPtr->pool, -1)); } } - + return result; } @@ -928,7 +939,7 @@ ServerMappedObjCmd(const ClientData UNUSED(clientData), Tcl_Interp *interp, int * Implements the "ns_server ... unmap ..." command. * * Results: - * Tcl result. + * Tcl result. * * Side effects: * might unmap a method/url pair. @@ -945,7 +956,7 @@ ServerUnmapObjCmd(const ClientData UNUSED(clientData), Tcl_Interp *interp, int o Ns_ObjvSpec lopts[] = { {"-noinherit", Ns_ObjvBool, &noinherit, INT2PTR(NS_TRUE)}, {NULL, NULL, NULL, NULL} - }; + }; Ns_ObjvSpec args[] = { {"mapspec", Ns_ObjvObj, &mapspecObj, NULL}, {NULL, NULL, NULL, NULL} @@ -954,7 +965,7 @@ ServerUnmapObjCmd(const ClientData UNUSED(clientData), Tcl_Interp *interp, int o NS_NONNULL_ASSERT(interp != NULL); NS_NONNULL_ASSERT(objv != NULL); NS_NONNULL_ASSERT(servPtr != NULL); - + if (Ns_ParseObjv(lopts, args, interp, objc-nargs, objc, objv) != NS_OK) { result = TCL_ERROR; } else if (MapspecParse(interp, mapspecObj, &method, &url) != NS_OK) { @@ -967,7 +978,7 @@ ServerUnmapObjCmd(const ClientData UNUSED(clientData), Tcl_Interp *interp, int o if (noinherit != 0) { flags |= NS_OP_NOINHERIT; } - + Ns_MutexLock(&servPtr->urlspace.lock); data = Ns_UrlSpecificDestroy(servPtr->server, method, url, poolid, flags); Ns_MutexUnlock(&servPtr->urlspace.lock); @@ -980,7 +991,83 @@ ServerUnmapObjCmd(const ClientData UNUSED(clientData), Tcl_Interp *interp, int o } Tcl_SetObjResult(interp, Tcl_NewBooleanObj(success)); } - + + return result; +} + + +/* + *---------------------------------------------------------------------- + * + * ServerListActive, ServerListQueued -- + * + * Implements the "ns_server ... active ..." and + * the "ns_server ... queued ..." command. + * + * Results: + * Tcl result. + * + * Side effects: + * Appends list data about active/queued connections to the Tcl_DString + * provided in the first argument. + * + *---------------------------------------------------------------------- + */ +static int +ServerListActive(Tcl_DString *dsPtr, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv, + ConnPool *poolPtr, int nargs) +{ + int result = TCL_OK, checkforproxy = (int)NS_FALSE; + Ns_ObjvSpec opts[] = { + {"-checkforproxy", Ns_ObjvBool, &checkforproxy, INT2PTR(NS_TRUE)}, + {NULL, NULL, NULL, NULL} + }; + + NS_NONNULL_ASSERT(dsPtr != NULL); + NS_NONNULL_ASSERT(interp != NULL); + NS_NONNULL_ASSERT(objv != NULL); + NS_NONNULL_ASSERT(poolPtr != NULL); + + if (Ns_ParseObjv(opts, NULL, interp, objc-nargs, objc, objv) != NS_OK) { + result = TCL_ERROR; + } else { + int i; + + Ns_MutexLock(&poolPtr->tqueue.lock); + for (i = 0; i < poolPtr->threads.max; i++) { + const ConnThreadArg *argPtr = &poolPtr->tqueue.args[i]; + + if (argPtr->connPtr != NULL) { + AppendConnList(dsPtr, argPtr->connPtr, "running", (bool)checkforproxy); + } + } + Ns_MutexUnlock(&poolPtr->tqueue.lock); + } + return result; +} + +static int +ServerListQueued(Tcl_DString *dsPtr, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv, + ConnPool *poolPtr, int nargs) +{ + int result = TCL_OK, checkforproxy = (int)NS_FALSE; + Ns_ObjvSpec opts[] = { + {"-checkforproxy", Ns_ObjvBool, &checkforproxy, INT2PTR(NS_TRUE)}, + {NULL, NULL, NULL, NULL} + }; + + NS_NONNULL_ASSERT(dsPtr != NULL); + NS_NONNULL_ASSERT(interp != NULL); + NS_NONNULL_ASSERT(objv != NULL); + NS_NONNULL_ASSERT(poolPtr != NULL); + + if (Ns_ParseObjv(opts, NULL, interp, objc-nargs, objc, objv) != NS_OK) { + result = TCL_ERROR; + } else { + Ns_MutexLock(&poolPtr->wqueue.lock); + AppendConnList(dsPtr, poolPtr->wqueue.wait.firstPtr, "queued", (bool)checkforproxy); + Ns_MutexUnlock(&poolPtr->wqueue.lock); + } return result; } @@ -1013,19 +1100,19 @@ NsTclServerObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj * Tcl_DString ds, *dsPtr = &ds; enum { - SActiveIdx, SAllIdx, SConnectionsIdx, + SActiveIdx, SAllIdx, SConnectionsIdx, SFiltersIdx, SKeepaliveIdx, SMapIdx, SMappedIdx, SMaxthreadsIdx, SMinthreadsIdx, - SPagedirIdx, SPoolsIdx, SQueuedIdx, + SPagedirIdx, SPoolsIdx, SQueuedIdx, SRequestprocsIdx, - SServerdirIdx, SStatsIdx, + SServerdirIdx, SStatsIdx, STcllibIdx, SThreadsIdx, STracesIdx, - SUnmapIdx, + SUnmapIdx, SUrl2fileIdx, SWaitingIdx }; - + static Ns_ObjvTable subcmds[] = { {"active", (unsigned int)SActiveIdx}, {"all", (unsigned int)SAllIdx}, @@ -1050,7 +1137,6 @@ NsTclServerObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj * {"waiting", (unsigned int)SWaitingIdx}, {NULL, 0u} }; - Ns_ObjvSpec opts[] = { {"-server", Ns_ObjvServer, &servPtr, NULL}, {"-pool", Ns_ObjvString, &pool, NULL}, @@ -1066,15 +1152,15 @@ NsTclServerObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj * if (unlikely(Ns_ParseObjv(opts, args, interp, 1, objc, objv) != NS_OK)) { return TCL_ERROR; } - - if ((subcmd == SPoolsIdx - || subcmd == SFiltersIdx - || subcmd == SPagedirIdx - || subcmd == SRequestprocsIdx - || subcmd == SUrl2fileIdx) - && pool != NULL) { - Ns_TclPrintfResult(interp, "option -pool is not allowed for this subcommand"); - return TCL_ERROR; + + if ((subcmd == SPoolsIdx + || subcmd == SFiltersIdx + || subcmd == SPagedirIdx + || subcmd == SRequestprocsIdx + || subcmd == SUrl2fileIdx) + && pool != NULL) { + Ns_TclPrintfResult(interp, "option -pool is not allowed for this subcommand"); + return TCL_ERROR; } if (subcmd != SMinthreadsIdx @@ -1082,20 +1168,23 @@ NsTclServerObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj * && subcmd != SMapIdx && subcmd != SMappedIdx && subcmd != SUnmapIdx + && subcmd != SActiveIdx + && subcmd != SQueuedIdx + && subcmd != SAllIdx ) { - /* - * Just for backwards compatibility - */ - if (nargs > 0) { - Ns_LogDeprecated(objv, objc, "ns_server ?-pool p? ...", - "Passing pool as second argument is deprecated."); + /* + * Just for backwards compatibility + */ + if (nargs > 0) { + Ns_LogDeprecated(objv, objc, "ns_server ?-pool p? ...", + "Passing pool as second argument is deprecated."); optArg = Tcl_GetString(objv[objc-1]); - pool = optArg; - } + pool = optArg; + } } if (servPtr == NULL) { - servPtr = itPtr->servPtr; + servPtr = itPtr->servPtr; } if (pool != NULL) { @@ -1110,13 +1199,13 @@ NsTclServerObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj * return TCL_ERROR; } } else { - poolPtr = servPtr->pools.defaultPtr; + poolPtr = servPtr->pools.defaultPtr; } - + switch (subcmd) { - /* - * These subcommands are server specific (do not allow -pool option) - */ + /* + * These subcommands are server specific (do not allow -pool option) + */ case SPoolsIdx: { Tcl_Obj *listObj = Tcl_NewListObj(0, NULL); @@ -1129,31 +1218,31 @@ NsTclServerObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj * break; case SFiltersIdx: - Tcl_DStringInit(dsPtr); + Tcl_DStringInit(dsPtr); NsGetFilters(dsPtr, servPtr->server); Tcl_DStringResult(interp, dsPtr); break; case SPagedirIdx: - Tcl_DStringInit(dsPtr); + Tcl_DStringInit(dsPtr); NsPageRoot(dsPtr, servPtr, NULL); Tcl_DStringResult(interp, dsPtr); break; case SServerdirIdx: - Tcl_DStringInit(dsPtr); - Tcl_DStringAppend(dsPtr, servPtr->fastpath.serverdir, -1); + Tcl_DStringInit(dsPtr); + Tcl_DStringAppend(dsPtr, servPtr->fastpath.serverdir, -1); Tcl_DStringResult(interp, dsPtr); break; case SRequestprocsIdx: - Tcl_DStringInit(dsPtr); + Tcl_DStringInit(dsPtr); NsGetRequestProcs(dsPtr, servPtr->server); Tcl_DStringResult(interp, dsPtr); break; case STracesIdx: - Tcl_DStringInit(dsPtr); + Tcl_DStringInit(dsPtr); NsGetTraces(dsPtr, servPtr->server); Tcl_DStringResult(interp, dsPtr); break; @@ -1163,27 +1252,27 @@ NsTclServerObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj * break; case SUrl2fileIdx: - Tcl_DStringInit(dsPtr); + Tcl_DStringInit(dsPtr); NsGetUrl2FileProcs(dsPtr, servPtr->server); Tcl_DStringResult(interp, dsPtr); break; - /* - * These subcommands are pool-specific (allow -pool option) - */ - + /* + * These subcommands are pool-specific (allow -pool option) + */ + case SWaitingIdx: Tcl_SetObjResult(interp, Tcl_NewIntObj(poolPtr->wqueue.wait.num)); break; case SKeepaliveIdx: - Ns_LogDeprecated(objv, objc, "ns_conn keepalive", NULL); + Ns_LogDeprecated(objv, objc, "ns_conn keepalive", NULL); Tcl_SetObjResult(interp, Tcl_NewIntObj(0)); break; case SMapIdx: result = ServerMapObjCmd(clientData, interp, objc, objv, servPtr, poolPtr, nargs); - break; + break; case SMappedIdx: result = ServerMappedObjCmd(clientData, interp, objc, objv, servPtr,nargs); @@ -1192,15 +1281,15 @@ NsTclServerObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj * case SUnmapIdx: result = ServerUnmapObjCmd(clientData, interp, objc, objv, servPtr, nargs); break; - + case SMaxthreadsIdx: result = ServerMaxThreadsObjCmd(clientData, interp, objc, objv, poolPtr, nargs); - break; + break; case SMinthreadsIdx: result = ServerMinThreadsObjCmd(clientData, interp, objc, objv, poolPtr, nargs); - break; - + break; + case SConnectionsIdx: Tcl_SetObjResult(interp, Tcl_NewLongObj((long)poolPtr->stats.processed)); break; @@ -1225,54 +1314,63 @@ NsTclServerObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj * Tcl_DStringAppendElement(dsPtr, buf); Ns_DStringAppend(dsPtr, " accepttime "); - Ns_DStringAppendTime(dsPtr, &poolPtr->stats.acceptTime); + Ns_DStringAppendTime(dsPtr, &poolPtr->stats.acceptTime); Ns_DStringAppend(dsPtr, " queuetime "); - Ns_DStringAppendTime(dsPtr, &poolPtr->stats.queueTime); + Ns_DStringAppendTime(dsPtr, &poolPtr->stats.queueTime); Ns_DStringAppend(dsPtr, " filtertime "); - Ns_DStringAppendTime(dsPtr, &poolPtr->stats.filterTime); + Ns_DStringAppendTime(dsPtr, &poolPtr->stats.filterTime); Ns_DStringAppend(dsPtr, " runtime "); - Ns_DStringAppendTime(dsPtr, &poolPtr->stats.runTime); + Ns_DStringAppendTime(dsPtr, &poolPtr->stats.runTime); Ns_DStringAppend(dsPtr, " tracetime "); - Ns_DStringAppendTime(dsPtr, &poolPtr->stats.traceTime); + Ns_DStringAppendTime(dsPtr, &poolPtr->stats.traceTime); Tcl_DStringResult(interp, dsPtr); break; case SThreadsIdx: Ns_TclPrintfResult(interp, - "min %d max %d current %d idle %d stopping 0", - poolPtr->threads.min, poolPtr->threads.max, - poolPtr->threads.current, poolPtr->threads.idle); + "min %d max %d current %d idle %d stopping 0", + poolPtr->threads.min, poolPtr->threads.max, + poolPtr->threads.current, poolPtr->threads.idle); break; case SActiveIdx: + Tcl_DStringInit(dsPtr); + result = ServerListActive(dsPtr, interp, objc, objv, poolPtr, nargs); + if (likely(result == NS_OK)) { + Tcl_DStringResult(interp, dsPtr); + } else { + Tcl_DStringFree(dsPtr); + } + break; + case SQueuedIdx: + Tcl_DStringInit(dsPtr); + result = ServerListQueued(dsPtr, interp, objc, objv, poolPtr, nargs); + if (likely(result == NS_OK)) { + Tcl_DStringResult(interp, dsPtr); + } else { + Tcl_DStringFree(dsPtr); + } + break; + case SAllIdx: Tcl_DStringInit(dsPtr); - if (subcmd != SQueuedIdx) { - int i; - - Ns_MutexLock(&poolPtr->tqueue.lock); - for (i=0; i < poolPtr->threads.max; i++) { - const ConnThreadArg *argPtr = &poolPtr->tqueue.args[i]; - - if (argPtr->connPtr != NULL) { - AppendConnList(dsPtr, argPtr->connPtr, "running"); - } - } - Ns_MutexUnlock(&poolPtr->tqueue.lock); + result = ServerListActive(dsPtr, interp, objc, objv, poolPtr, nargs); + if (likely(result == NS_OK)) { + ServerListQueued(dsPtr, interp, objc, objv, poolPtr, nargs); } - if (subcmd != SActiveIdx) { - Ns_MutexLock(&poolPtr->wqueue.lock); - AppendConnList(dsPtr, poolPtr->wqueue.wait.firstPtr, "queued"); - Ns_MutexUnlock(&poolPtr->wqueue.lock); + if (likely(result == NS_OK)) { + Tcl_DStringResult(interp, dsPtr); + } else { + Tcl_DStringFree(dsPtr); } - Tcl_DStringResult(interp, dsPtr); break; + default: /* should never happen */ assert(subcmd && 0); @@ -1310,7 +1408,7 @@ NsStartServer(const NsServer *servPtr) while (poolPtr != NULL) { poolPtr->threads.idle = 0; poolPtr->threads.current = poolPtr->threads.min; - poolPtr->threads.creating = poolPtr->threads.min; + poolPtr->threads.creating = poolPtr->threads.min; for (n = 0; n < poolPtr->threads.min; ++n) { CreateConnThread(poolPtr); } @@ -1344,14 +1442,14 @@ WakeupConnThreads(ConnPool *poolPtr) { Ns_MutexLock(&poolPtr->tqueue.lock); for (i = 0; i < poolPtr->threads.max; i++) { - ConnThreadArg *argPtr = &poolPtr->tqueue.args[i]; - - if (argPtr->state == connThread_idle) { - assert(argPtr->connPtr == NULL); - Ns_MutexLock(&argPtr->lock); - Ns_CondSignal(&argPtr->cond); - Ns_MutexUnlock(&argPtr->lock); - } + ConnThreadArg *argPtr = &poolPtr->tqueue.args[i]; + + if (argPtr->state == connThread_idle) { + assert(argPtr->connPtr == NULL); + Ns_MutexLock(&argPtr->lock); + Ns_CondSignal(&argPtr->cond); + Ns_MutexUnlock(&argPtr->lock); + } } Ns_MutexUnlock(&poolPtr->tqueue.lock); } @@ -1384,7 +1482,7 @@ NsStopServer(NsServer *servPtr) servPtr->pools.shutdown = NS_TRUE; poolPtr = servPtr->pools.firstPtr; while (poolPtr != NULL) { - WakeupConnThreads(poolPtr); + WakeupConnThreads(poolPtr); poolPtr = poolPtr->nextPtr; } } @@ -1451,14 +1549,54 @@ NsConnArgProc(Tcl_DString *dsPtr, const void *arg) if (arg != NULL) { ConnPool *poolPtr = argPtr->poolPtr; - Ns_MutexLock(&poolPtr->tqueue.lock); - AppendConn(dsPtr, argPtr->connPtr, "running"); - Ns_MutexUnlock(&poolPtr->tqueue.lock); + Ns_MutexLock(&poolPtr->tqueue.lock); + AppendConn(dsPtr, argPtr->connPtr, "running", NS_FALSE); + Ns_MutexUnlock(&poolPtr->tqueue.lock); } else { Tcl_DStringAppendElement(dsPtr, ""); } } + +/* + *---------------------------------------------------------------------- + * + * ConnThreadSetName -- + * + * Set the conn thread name based on server name, pool name, threadID and + * connID. The pool name is always non-null, but might have an empty + * string as "name". + * + * Results: + * None. + * + * Side effects: + * Update thread name (internally just a printf operation) + * + *---------------------------------------------------------------------- + */ + +static void +ConnThreadSetName(const char *server, const char *pool, uintptr_t threadId, uintptr_t connId) +{ + NS_NONNULL_ASSERT(server != NULL); + NS_NONNULL_ASSERT(pool != NULL); + + if (*pool != '\0') { + /* + * Non-Empty pool name. + */ + Ns_ThreadSetName("-conn:%s:%s:%" PRIuPTR ":%" PRIuPTR "-", + server, pool, threadId, connId); + } else { + /* + * Empty pool name. + */ + Ns_ThreadSetName("-conn:%s:%" PRIuPTR ":%" PRIuPTR "-", + server, threadId, connId); + } +} + /* *---------------------------------------------------------------------- @@ -1484,7 +1622,7 @@ NsConnThread(void *arg) NsServer *servPtr = poolPtr->servPtr; Conn *connPtr = NULL; Ns_Time wait, *timePtr = &wait; - uintptr_t id; + uintptr_t threadId; bool shutdown, fromQueue; int cpt, ncons, current; Ns_ReturnCode status = NS_OK; @@ -1508,29 +1646,17 @@ NsConnThread(void *arg) Ns_MutexUnlock(tqueueLockPtr); Ns_MutexLock(threadsLockPtr); - id = poolPtr->threads.nextid++; + threadId = poolPtr->threads.nextid++; if (poolPtr->threads.creating > 0) { poolPtr->threads.creating--; } Ns_MutexUnlock(threadsLockPtr); - /* - * Set the conn thread name. The pool name is always non-null, but - * might have an empty string as "name". - */ - assert(poolPtr->pool != NULL); - { - const char *p = *poolPtr->pool != '\0' ? poolPtr->pool : NULL; - Ns_ThreadSetName("-conn:%s%s%s:%" PRIuPTR "-", - servPtr->server, - (p != NULL) ? ":" : "", - (p != NULL) ? p : "", - id); - } + ConnThreadSetName(servPtr->server, poolPtr->pool, threadId, 0); Ns_ThreadSelf(&joinThread); /*fprintf(stderr, "### starting conn thread %p <%s>\n", (void *)joinThread, Ns_ThreadGetName());*/ - + cpt = poolPtr->threads.connsperthread; ncons = cpt; timeout = poolPtr->threads.timeout; @@ -1540,17 +1666,17 @@ NsConnThread(void *arg) * the initialization delay when the first connection comes in. */ { - Tcl_Interp *interp; - Ns_Time start, end, diff; + Tcl_Interp *interp; + Ns_Time start, end, diff; Ns_GetTime(&start); - interp = NsTclAllocateInterp(servPtr); + interp = NsTclAllocateInterp(servPtr); Ns_GetTime(&end); Ns_DiffTime(&end, &start, &diff); - Ns_Log(Notice, "thread initialized (%" PRIu64 ".%06ld secs)", - (int64_t)diff.sec, diff.usec); - Ns_TclDeAllocateInterp(interp); - argPtr->state = connThread_ready; + Ns_Log(Notice, "thread initialized (%" PRIu64 ".%06ld secs)", + (int64_t)diff.sec, diff.usec); + Ns_TclDeAllocateInterp(interp); + argPtr->state = connThread_ready; } /* @@ -1559,99 +1685,99 @@ NsConnThread(void *arg) for (;;) { - /* - * We are ready to process requests. Pick it either a request - * from the waiting queue, or go to a waiting state and add - * jourself to the conn thread queue. - */ - assert(argPtr->connPtr == NULL); - assert(argPtr->state == connThread_ready); - - if (poolPtr->wqueue.wait.firstPtr != NULL) { - connPtr = NULL; - Ns_MutexLock(wqueueLockPtr); - if (poolPtr->wqueue.wait.firstPtr != NULL) { - /* - * There are waiting requests. Pull the first connection of - * the waiting list and assign it to the ConnThreadArg. - */ - connPtr = poolPtr->wqueue.wait.firstPtr; - poolPtr->wqueue.wait.firstPtr = connPtr->nextPtr; - if (poolPtr->wqueue.wait.lastPtr == connPtr) { - poolPtr->wqueue.wait.lastPtr = NULL; - } - connPtr->nextPtr = NULL; - poolPtr->wqueue.wait.num --; - } - Ns_MutexUnlock(wqueueLockPtr); - - argPtr->connPtr = connPtr; - fromQueue = NS_TRUE; - } else { - fromQueue = NS_FALSE; - } - - if (argPtr->connPtr == NULL) { - /* - * There is nothing urgent to do. We can add ourself to the - * conn thread queue. - */ - Ns_MutexLock(threadsLockPtr); - poolPtr->threads.idle ++; - Ns_MutexUnlock(threadsLockPtr); - - Ns_MutexLock(tqueueLockPtr); - argPtr->state = connThread_idle; - /* - * We put an entry into the thread queue. However, we must - * take care, that signals are not sent, before this thread - * is waiting for it. Therefore we lock the connection - * thread specific lock right here, also the signal sending - * code uses the same lock. - */ - Ns_MutexLock(&argPtr->lock); - - argPtr->nextPtr = poolPtr->tqueue.nextPtr; - poolPtr->tqueue.nextPtr = argPtr; - Ns_MutexUnlock(tqueueLockPtr); - - /* - * Wait until someone wakes us up, or a timeout happens. - */ - while (!servPtr->pools.shutdown) { - - Ns_GetTime(timePtr); - Ns_IncrTime(timePtr, timeout, 0); - - status = Ns_CondTimedWait(&argPtr->cond, &argPtr->lock, timePtr); - - if (status == NS_TIMEOUT) { - if (argPtr->connPtr != NULL) { - /* + /* + * We are ready to process requests. Pick it either a request + * from the waiting queue, or go to a waiting state and add + * jourself to the conn thread queue. + */ + assert(argPtr->connPtr == NULL); + assert(argPtr->state == connThread_ready); + + if (poolPtr->wqueue.wait.firstPtr != NULL) { + connPtr = NULL; + Ns_MutexLock(wqueueLockPtr); + if (poolPtr->wqueue.wait.firstPtr != NULL) { + /* + * There are waiting requests. Pull the first connection of + * the waiting list and assign it to the ConnThreadArg. + */ + connPtr = poolPtr->wqueue.wait.firstPtr; + poolPtr->wqueue.wait.firstPtr = connPtr->nextPtr; + if (poolPtr->wqueue.wait.lastPtr == connPtr) { + poolPtr->wqueue.wait.lastPtr = NULL; + } + connPtr->nextPtr = NULL; + poolPtr->wqueue.wait.num --; + } + Ns_MutexUnlock(wqueueLockPtr); + + argPtr->connPtr = connPtr; + fromQueue = NS_TRUE; + } else { + fromQueue = NS_FALSE; + } + + if (argPtr->connPtr == NULL) { + /* + * There is nothing urgent to do. We can add ourself to the + * conn thread queue. + */ + Ns_MutexLock(threadsLockPtr); + poolPtr->threads.idle ++; + Ns_MutexUnlock(threadsLockPtr); + + Ns_MutexLock(tqueueLockPtr); + argPtr->state = connThread_idle; + /* + * We put an entry into the thread queue. However, we must + * take care, that signals are not sent, before this thread + * is waiting for it. Therefore we lock the connection + * thread specific lock right here, also the signal sending + * code uses the same lock. + */ + Ns_MutexLock(&argPtr->lock); + + argPtr->nextPtr = poolPtr->tqueue.nextPtr; + poolPtr->tqueue.nextPtr = argPtr; + Ns_MutexUnlock(tqueueLockPtr); + + /* + * Wait until someone wakes us up, or a timeout happens. + */ + while (!servPtr->pools.shutdown) { + + Ns_GetTime(timePtr); + Ns_IncrTime(timePtr, timeout, 0); + + status = Ns_CondTimedWait(&argPtr->cond, &argPtr->lock, timePtr); + + if (status == NS_TIMEOUT) { + if (argPtr->connPtr != NULL) { + /* * This should not happen: we had a timeout, but there * is a connection to be handled; when a connection * comes in, we get signaled and should see therefore * no timeout. Maybe the signal was lost? */ - Ns_Log(Warning, "signal lost, resuming after timeout"); - status = NS_OK; + Ns_Log(Warning, "signal lost, resuming after timeout"); + status = NS_OK; - } else if (poolPtr->threads.current <= poolPtr->threads.min) { - /* + } else if (poolPtr->threads.current <= poolPtr->threads.min) { + /* * We have a timeout, but we should not reduce the * number of threads below min-threads. */ continue; - + } else { - /* - * We have a timeout, and the thread can exit + /* + * We have a timeout, and the thread can exit */ break; } - } + } - if (argPtr->connPtr != NULL) { + if (argPtr->connPtr != NULL) { /* * We got something to do */ @@ -1659,59 +1785,88 @@ NsConnThread(void *arg) } Ns_Log(Debug, "Unexpected condition after CondTimedWait; maybe shutdown?"); - } - - Ns_MutexUnlock(&argPtr->lock); - - assert(argPtr->state == connThread_idle); - - if (argPtr->connPtr == NULL) { - /* - * We were not signaled on purpose, so we have to dequeue - * the current thread. - */ - ConnThreadArg *aPtr, **prevPtr; - - Ns_MutexLock(tqueueLockPtr); - for (aPtr = poolPtr->tqueue.nextPtr, prevPtr = &poolPtr->tqueue.nextPtr; - aPtr != NULL; - prevPtr = &aPtr->nextPtr, aPtr = aPtr->nextPtr) { - if (aPtr == argPtr) { - *prevPtr = aPtr->nextPtr; - argPtr->nextPtr = NULL; - break; - } - } - argPtr->state = connThread_busy; - Ns_MutexUnlock(tqueueLockPtr); - } else { - Ns_MutexLock(tqueueLockPtr); - argPtr->state = connThread_busy; - Ns_MutexUnlock(tqueueLockPtr); - } - - Ns_MutexLock(threadsLockPtr); - poolPtr->threads.idle --; - Ns_MutexUnlock(threadsLockPtr); - - if (servPtr->pools.shutdown) { - exitMsg = "shutdown pending"; - break; - } else if (status == NS_TIMEOUT) { - exitMsg = "idle thread terminates"; - break; - } - } - - connPtr = argPtr->connPtr; - assert(connPtr != NULL); - - Ns_GetTime(&connPtr->requestDequeueTime); - - /* - * Run the connection. - */ - ConnRun(argPtr, connPtr); + } + + Ns_MutexUnlock(&argPtr->lock); + + assert(argPtr->state == connThread_idle); + + if (argPtr->connPtr == NULL) { + /* + * We were not signaled on purpose, so we have to dequeue + * the current thread. + */ + ConnThreadArg *aPtr, **prevPtr; + + Ns_MutexLock(tqueueLockPtr); + for (aPtr = poolPtr->tqueue.nextPtr, prevPtr = &poolPtr->tqueue.nextPtr; + aPtr != NULL; + prevPtr = &aPtr->nextPtr, aPtr = aPtr->nextPtr) { + if (aPtr == argPtr) { + *prevPtr = aPtr->nextPtr; + argPtr->nextPtr = NULL; + break; + } + } + argPtr->state = connThread_busy; + Ns_MutexUnlock(tqueueLockPtr); + } else { + Ns_MutexLock(tqueueLockPtr); + argPtr->state = connThread_busy; + Ns_MutexUnlock(tqueueLockPtr); + } + + Ns_MutexLock(threadsLockPtr); + poolPtr->threads.idle --; + Ns_MutexUnlock(threadsLockPtr); + + if (servPtr->pools.shutdown) { + exitMsg = "shutdown pending"; + break; + } else if (status == NS_TIMEOUT) { + exitMsg = "idle thread terminates"; + break; + } + } + + connPtr = argPtr->connPtr; + assert(connPtr != NULL); + + Ns_GetTime(&connPtr->requestDequeueTime); + + /* + * Run the connection if possible (requires a valid sockPtr and a + * successful NsGetRequest() operation). + */ + if (likely(connPtr->sockPtr != NULL)) { + /* + * Get the request from the sockPtr (either from read-ahead or via + * parsing). + */ + connPtr->reqPtr = NsGetRequest(connPtr->sockPtr, &connPtr->requestDequeueTime); + + /* + * If there is no request, produce a warning and close the + * connection. + */ + if (connPtr->reqPtr == NULL) { + Ns_Log(Warning, "connPtr %p has no reqPtr, close this connection", (void *)connPtr); + (void) Ns_ConnClose((Ns_Conn *)connPtr); + } else { + /* + * Everything is supplied, run the request. ConnRun() + * closes finally the connection. + */ + ConnThreadSetName(servPtr->server, poolPtr->pool, threadId, connPtr->id); + ConnRun(connPtr); + } + } else { + /* + * If we have no sockPtr, we can't do much here. + */ + Ns_Log(Warning, "connPtr %p has no socket, close this connection", (void *)connPtr); + (void) Ns_ConnClose((Ns_Conn *)connPtr); + } /* * push connection to the free list. @@ -1725,90 +1880,90 @@ NsConnThread(void *arg) connPtr->nextPtr->prevPtr = connPtr->prevPtr; } connPtr->prevPtr = NULL; - + Ns_SetFree(connPtr->headers); connPtr->headers = NULL; Ns_MutexLock(wqueueLockPtr); connPtr->nextPtr = poolPtr->wqueue.freePtr; poolPtr->wqueue.freePtr = connPtr; - Ns_MutexUnlock(wqueueLockPtr); + Ns_MutexUnlock(wqueueLockPtr); + + Ns_MutexLock(tqueueLockPtr); + argPtr->state = connThread_ready; + Ns_MutexUnlock(tqueueLockPtr); - Ns_MutexLock(tqueueLockPtr); - argPtr->state = connThread_ready; - Ns_MutexUnlock(tqueueLockPtr); + if (cpt != 0) { + int waiting, idle, lowwater; - if (cpt != 0) { - int waiting, idle, lowwater; + --ncons; - --ncons; - - /* - * Get a consistent snapshot of the controlling variables. - */ + /* + * Get a consistent snapshot of the controlling variables. + */ Ns_MutexLock(wqueueLockPtr); Ns_MutexLock(threadsLockPtr); - waiting = poolPtr->wqueue.wait.num; - lowwater = poolPtr->wqueue.lowwatermark; - idle = poolPtr->threads.idle; - current = poolPtr->threads.current; + waiting = poolPtr->wqueue.wait.num; + lowwater = poolPtr->wqueue.lowwatermark; + idle = poolPtr->threads.idle; + current = poolPtr->threads.current; Ns_MutexUnlock(threadsLockPtr); Ns_MutexUnlock(wqueueLockPtr); - if (Ns_LogSeverityEnabled(Debug)) { - Ns_Time now, acceptTime, queueTime, filterTime, netRunTime, runTime, fullTime; - - Ns_DiffTime(&connPtr->requestQueueTime, &connPtr->acceptTime, &acceptTime); - Ns_DiffTime(&connPtr->requestDequeueTime, &connPtr->requestQueueTime, &queueTime); - Ns_DiffTime(&connPtr->filterDoneTime, &connPtr->requestDequeueTime, &filterTime); - - Ns_GetTime(&now); - Ns_DiffTime(&now, &connPtr->requestDequeueTime, &runTime); - Ns_DiffTime(&now, &connPtr->filterDoneTime, &netRunTime); - Ns_DiffTime(&now, &connPtr->requestQueueTime, &fullTime); - - Ns_Log(Debug, "[%ld] end of job, waiting %d current %d idle %d ncons %d fromQueue %d" - " start %" PRIu64 ".%06ld" - " %" PRIu64 ".%06ld" - " accept %" PRIu64 ".%06ld" - " queue %" PRIu64 ".%06ld" - " filter %" PRIu64 ".%06ld" - " run %" PRIu64 ".%06ld" - " netrun %" PRIu64 ".%06ld" - " total %" PRIu64 ".%06ld", - ThreadNr(poolPtr, argPtr), - waiting, poolPtr->threads.current, idle, ncons, fromQueue ? 1 : 0, - (int64_t) connPtr->acceptTime.sec, connPtr->acceptTime.usec, - (int64_t) connPtr->requestQueueTime.sec, connPtr->requestQueueTime.usec, - (int64_t) acceptTime.sec, acceptTime.usec, - (int64_t) queueTime.sec, queueTime.usec, - (int64_t) filterTime.sec, filterTime.usec, - (int64_t) runTime.sec, runTime.usec, - (int64_t) netRunTime.sec, netRunTime.usec, - (int64_t) fullTime.sec, fullTime.usec - ); - } - - if (waiting > 0) { - /* - * There are waiting requests. Work on those unless we - * are expiring or we are already under the lowwater - * mark of connection threads, or we are the last man - * standing. - */ - if (ncons > 0 || waiting > lowwater || current <= 1) { - continue; - } - } - - if (ncons <= 0) { - exitMsg = "exceeded max connections per thread"; - break; - } - } else if (ncons <= 0) { - /* Served given # of connections in this thread */ - exitMsg = "exceeded max connections per thread"; - break; + if (Ns_LogSeverityEnabled(Debug)) { + Ns_Time now, acceptTime, queueTime, filterTime, netRunTime, runTime, fullTime; + + Ns_DiffTime(&connPtr->requestQueueTime, &connPtr->acceptTime, &acceptTime); + Ns_DiffTime(&connPtr->requestDequeueTime, &connPtr->requestQueueTime, &queueTime); + Ns_DiffTime(&connPtr->filterDoneTime, &connPtr->requestDequeueTime, &filterTime); + + Ns_GetTime(&now); + Ns_DiffTime(&now, &connPtr->requestDequeueTime, &runTime); + Ns_DiffTime(&now, &connPtr->filterDoneTime, &netRunTime); + Ns_DiffTime(&now, &connPtr->requestQueueTime, &fullTime); + + Ns_Log(Debug, "[%ld] end of job, waiting %d current %d idle %d ncons %d fromQueue %d" + " start %" PRIu64 ".%06ld" + " %" PRIu64 ".%06ld" + " accept %" PRIu64 ".%06ld" + " queue %" PRIu64 ".%06ld" + " filter %" PRIu64 ".%06ld" + " run %" PRIu64 ".%06ld" + " netrun %" PRIu64 ".%06ld" + " total %" PRIu64 ".%06ld", + ThreadNr(poolPtr, argPtr), + waiting, poolPtr->threads.current, idle, ncons, fromQueue ? 1 : 0, + (int64_t) connPtr->acceptTime.sec, connPtr->acceptTime.usec, + (int64_t) connPtr->requestQueueTime.sec, connPtr->requestQueueTime.usec, + (int64_t) acceptTime.sec, acceptTime.usec, + (int64_t) queueTime.sec, queueTime.usec, + (int64_t) filterTime.sec, filterTime.usec, + (int64_t) runTime.sec, runTime.usec, + (int64_t) netRunTime.sec, netRunTime.usec, + (int64_t) fullTime.sec, fullTime.usec + ); + } + + if (waiting > 0) { + /* + * There are waiting requests. Work on those unless we + * are expiring or we are already under the lowwater + * mark of connection threads, or we are the last man + * standing. + */ + if (ncons > 0 || waiting > lowwater || current <= 1) { + continue; + } + } + + if (ncons <= 0) { + exitMsg = "exceeded max connections per thread"; + break; + } + } else if (ncons <= 0) { + /* Served given # of connections in this thread */ + exitMsg = "exceeded max connections per thread"; + break; } } argPtr->state = connThread_dead; @@ -1819,35 +1974,35 @@ NsConnThread(void *arg) { - bool wakeup; - /* - * Record the fact that this driver is exiting by decrementing the - * actually running threads and wakeup the driver to check against - * thread starvation (due to an insufficient number of connection - * threads). - */ - Ns_MutexLock(threadsLockPtr); - poolPtr->threads.current--; - wakeup = (poolPtr->threads.current < poolPtr->threads.min); - Ns_MutexUnlock(threadsLockPtr); - - /* - * During shutdown, we do not want to restart connection - * threads. The driver pointer might be already invalid. - */ - if (wakeup && connPtr != NULL && !shutdown) { + bool wakeup; + /* + * Record the fact that this driver is exiting by decrementing the + * actually running threads and wakeup the driver to check against + * thread starvation (due to an insufficient number of connection + * threads). + */ + Ns_MutexLock(threadsLockPtr); + poolPtr->threads.current--; + wakeup = (poolPtr->threads.current < poolPtr->threads.min); + Ns_MutexUnlock(threadsLockPtr); + + /* + * During shutdown, we do not want to restart connection + * threads. The driver pointer might be already invalid. + */ + if (wakeup && connPtr != NULL && !shutdown) { assert(connPtr->drvPtr != NULL); - NsWakeupDriver(connPtr->drvPtr); - } + NsWakeupDriver(connPtr->drvPtr); + } } - + /* * During shutdown, the main thread waits for signals on the * condition variable to check whether all threads have terminated * already. */ if (shutdown) { - Ns_CondSignal(&poolPtr->wqueue.cond); + Ns_CondSignal(&poolPtr->wqueue.cond); } Ns_MutexLock(&servPtr->pools.lock); @@ -1857,15 +2012,15 @@ NsConnThread(void *arg) /*fprintf(stderr, "###stopping joinThread %p, self %p\n", joinThread, servPtr->pools.joinThread);*/ - + if (joinThread != NULL) { Ns_ThreadJoin(&joinThread, NULL); } - + Ns_Log(Notice, "exiting: %s", exitMsg); Ns_MutexLock(tqueueLockPtr); - argPtr->state = connThread_free; + argPtr->state = connThread_free; Ns_MutexUnlock(tqueueLockPtr); Ns_ThreadExit(argPtr); @@ -1877,47 +2032,31 @@ NsConnThread(void *arg) * * ConnRun -- * - * Run a valid connection. + * Run the actual non-null request and close it finally the connection. * * Results: * None. * * Side effects: - * Connection request is read and parsed and the corresponding - * service routine is called. + * Potential side-effects caused by the callbacks. * *---------------------------------------------------------------------- */ + static void -ConnRun(const ConnThreadArg *UNUSED(argPtr), Conn *connPtr) +ConnRun(Conn *connPtr) { + Sock *sockPtr; Ns_Conn *conn; const NsServer *servPtr; Ns_ReturnCode status = NS_OK; - Sock *sockPtr; char *auth; - /*NS_NONNULL_ASSERT(argPtr != NULL);*/ NS_NONNULL_ASSERT(connPtr != NULL); - conn = (Ns_Conn *) connPtr; - servPtr = connPtr->poolPtr->servPtr; + conn = (Ns_Conn *)connPtr; sockPtr = connPtr->sockPtr; - - /* - * Re-initialize and run the connection. - */ - if (sockPtr != NULL) { - connPtr->reqPtr = NsGetRequest(sockPtr, &connPtr->requestDequeueTime); - } else { - connPtr->reqPtr = NULL; - } - if (connPtr->reqPtr == NULL) { - Ns_Log(Warning, "connPtr %p has no reqPtr, close this connection", (void *)connPtr); - (void) Ns_ConnClose(conn); - return; - } assert(sockPtr != NULL); assert(sockPtr->reqPtr != NULL); @@ -1936,7 +2075,7 @@ ConnRun(const ConnThreadArg *UNUSED(argPtr), Conn *connPtr) /*{ConnPool *poolPtr = argPtr->poolPtr; Ns_Log(Notice, "ConnRun [%d] connPtr %p req %p %s", ThreadNr(poolPtr, argPtr), connPtr, connPtr->request, connPtr->request.line); - } */ + } */ connPtr->headers = Ns_SetRecreate(connPtr->reqPtr->headers); connPtr->contentLength = connPtr->reqPtr->length; @@ -1945,9 +2084,12 @@ ConnRun(const ConnThreadArg *UNUSED(argPtr), Conn *connPtr) connPtr->responseLength = -1; /* -1 == unknown (stream), 0 == zero bytes. */ connPtr->recursionCount = 0; connPtr->auth = NULL; + /* + * keep == -1 means: Undecided, the default keep-alive rules are applied. + */ + connPtr->keep = -1; - connPtr->keep = -1; /* Undecided, default keep-alive rules apply */ - + servPtr = connPtr->poolPtr->servPtr; Ns_ConnSetCompression(conn, servPtr->compress.enable ? servPtr->compress.level : 0); connPtr->compress = -1; @@ -1955,7 +2097,10 @@ ConnRun(const ConnThreadArg *UNUSED(argPtr), Conn *connPtr) connPtr->urlEncoding = servPtr->encoding.urlEncoding; Tcl_InitHashTable(&connPtr->files, TCL_STRING_KEYS); - snprintf(connPtr->idstr, sizeof(connPtr->idstr), "cns%" PRIuPTR, connPtr->id); + + memcpy(connPtr->idstr, "cns", 3u); + ns_uint64toa(&connPtr->idstr[3], connPtr->id); + connPtr->outputheaders = Ns_SetCreate(NULL); if (connPtr->request.version < 1.0) { conn->flags |= NS_CONN_SKIPHDRS; @@ -1978,7 +2123,7 @@ ConnRun(const ConnThreadArg *UNUSED(argPtr), Conn *connPtr) if ((conn->request.method != NULL) && STREQ(conn->request.method, "HEAD")) { conn->flags |= NS_CONN_SKIPBODY; } - + if (sockPtr->drvPtr->requestProc != NULL) { /* * Run the driver's private handler @@ -2072,7 +2217,7 @@ ConnRun(const ConnThreadArg *UNUSED(argPtr), Conn *connPtr) /* * In case some leftover is in the buffer, signal the driver to * process the remaining bytes. - * + * */ if ((sockPtr->keep) && (connPtr->reqPtr->leftover > 0u)) { NsWakeupDriver(sockPtr->drvPtr); @@ -2114,7 +2259,7 @@ ConnRun(const ConnThreadArg *UNUSED(argPtr), Conn *connPtr) connPtr->auth = NULL; Ns_SetFree(connPtr->outputheaders); connPtr->outputheaders = NULL; - + if (connPtr->request.line != NULL) { /* * reqPtr is freed by FreeRequest() in the driver. @@ -2158,11 +2303,11 @@ CreateConnThread(ConnPool *poolPtr) #if !defined(NDEBUG) { const char *threadName = Ns_ThreadGetName(); - assert(strncmp("-driver:", threadName, 8u) == 0 - || strncmp("-main-", threadName, 6u) == 0 - || strncmp("-spooler", threadName, 8u) == 0 - || strncmp("-service-", threadName, 9u) == 0 - ); + assert(strncmp("-driver:", threadName, 8u) == 0 + || strncmp("-main-", threadName, 6u) == 0 + || strncmp("-spooler", threadName, 8u) == 0 + || strncmp("-service-", threadName, 9u) == 0 + ); } #endif @@ -2180,8 +2325,8 @@ CreateConnThread(ConnPool *poolPtr) Ns_MutexLock(&poolPtr->tqueue.lock); for (i = 0; likely(i < poolPtr->threads.max); i++) { if (poolPtr->tqueue.args[i].state == connThread_free) { - argPtr = &(poolPtr->tqueue.args[i]); - break; + argPtr = &(poolPtr->tqueue.args[i]); + break; } } if (likely(argPtr != NULL)) { @@ -2195,16 +2340,16 @@ CreateConnThread(ConnPool *poolPtr) argPtr->connPtr = NULL; argPtr->nextPtr = NULL; argPtr->cond = NULL; - + Ns_ThreadCreate(NsConnThread, argPtr, 0, &thread); } else { Ns_MutexUnlock(&poolPtr->tqueue.lock); - + Ns_MutexLock(&poolPtr->threads.lock); - poolPtr->threads.current --; - poolPtr->threads.creating --; + poolPtr->threads.current --; + poolPtr->threads.creating --; Ns_MutexUnlock(&poolPtr->threads.lock); - + Ns_Log(Notice, "Cannot create connection thread, all available slots (%d) are used\n", i); } } @@ -2227,7 +2372,7 @@ CreateConnThread(ConnPool *poolPtr) */ static void -AppendConn(Tcl_DString *dsPtr, const Conn *connPtr, const char *state) +AppendConn(Tcl_DString *dsPtr, const Conn *connPtr, const char *state, bool checkforproxy) { Ns_Time now, diff; @@ -2241,27 +2386,41 @@ AppendConn(Tcl_DString *dsPtr, const Conn *connPtr, const char *state) */ if (connPtr != NULL) { Tcl_DStringAppendElement(dsPtr, connPtr->idstr); - if (connPtr->reqPtr != NULL) { - Tcl_DStringAppendElement(dsPtr, Ns_ConnPeer((const Ns_Conn *) connPtr)); - } else { - /* Actually, this should not happen, but it does, maybe due - * to the above mentioned race condition; we notice in the - * errlog the fact and return a placeholder value - */ - Ns_Log(Notice, "AppendConn: no reqPtr in state %s; ignore conn in output", state); - Tcl_DStringAppendElement(dsPtr, "unknown"); - } + if (connPtr->reqPtr != NULL) { + const char *p; + + if ( checkforproxy ) { + p = Ns_SetIGet(connPtr->headers, "X-Forwarded-For"); + if (p == NULL || (*p != '\0') || strcasecmp(p, "unknown") == 0) { + /* + * Lookup of header field failed, use upstream peer + * address. + */ + p = Ns_ConnPeer((const Ns_Conn *) connPtr); + } + } else { + p = Ns_ConnPeer((const Ns_Conn *) connPtr); + } + Tcl_DStringAppendElement(dsPtr, p); + } else { + /* Actually, this should not happen, but it does, maybe due + * to the above mentioned race condition; we notice in the + * errlog the fact and return a placeholder value + */ + Ns_Log(Notice, "AppendConn: no reqPtr in state %s; ignore conn in output", state); + Tcl_DStringAppendElement(dsPtr, "unknown"); + } Tcl_DStringAppendElement(dsPtr, state); if (connPtr->request.line != NULL) { Tcl_DStringAppendElement(dsPtr, (connPtr->request.method != NULL) ? connPtr->request.method : "?"); Tcl_DStringAppendElement(dsPtr, (connPtr->request.url != NULL) ? connPtr->request.url : "?"); - } else { - Ns_Log(Notice, "AppendConn: no request in state %s; ignore conn in output", state); - Tcl_DStringAppendElement(dsPtr, "unknown"); - Tcl_DStringAppendElement(dsPtr, "unknown"); - } - Ns_GetTime(&now); + } else { + Ns_Log(Notice, "AppendConn: no request in state %s; ignore conn in output", state); + Tcl_DStringAppendElement(dsPtr, "unknown"); + Tcl_DStringAppendElement(dsPtr, "unknown"); + } + Ns_GetTime(&now); Ns_DiffTime(&now, &connPtr->requestQueueTime, &diff); Ns_DStringNAppend(dsPtr, " ", 1); Ns_DStringAppendTime(dsPtr, &diff); @@ -2288,13 +2447,13 @@ AppendConn(Tcl_DString *dsPtr, const Conn *connPtr, const char *state) */ static void -AppendConnList(Tcl_DString *dsPtr, const Conn *firstPtr, const char *state) +AppendConnList(Tcl_DString *dsPtr, const Conn *firstPtr, const char *state, bool checkforproxy) { NS_NONNULL_ASSERT(dsPtr != NULL); NS_NONNULL_ASSERT(state != NULL); while (firstPtr != NULL) { - AppendConn(dsPtr, firstPtr, state); + AppendConn(dsPtr, firstPtr, state, checkforproxy); firstPtr = firstPtr->nextPtr; } } diff --git a/nsd/quotehtml.c b/nsd/quotehtml.c index cf8948df..95f09635 100644 --- a/nsd/quotehtml.c +++ b/nsd/quotehtml.c @@ -11,7 +11,7 @@ * * The Original Code is AOLserver Code and related documentation * distributed by AOL. - * + * * The Initial Developer of the Original Code is America Online, * Inc. Portions created by AOL are Copyright (C) 1999 America Online, * Inc. All Rights Reserved. @@ -31,11 +31,18 @@ /* * quotehtml.c -- * - * Take text and make it safe for HTML. + * Take text and make it safe for HTML. */ #include "nsd.h" +/* + * Static functions defined in this file. + */ +static void +QuoteHtml(Ns_DString *dsPtr, const char *breakChar, const char *htmlString) + NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(3); + /* *---------------------------------------------------------------------- @@ -52,56 +59,97 @@ * *---------------------------------------------------------------------- */ - -void -Ns_QuoteHtml(Ns_DString *dsPtr, const char *htmlString) +static void +QuoteHtml(Ns_DString *dsPtr, const char *breakChar, const char *htmlString) { + const char *toProcess = htmlString; + NS_NONNULL_ASSERT(dsPtr != NULL); + NS_NONNULL_ASSERT(breakChar != NULL); NS_NONNULL_ASSERT(htmlString != NULL); - - while (likely(*htmlString != '\0')) { - switch (*htmlString) { + + do { + /* + * Append the first part, escape the protected char, and + * continue. + */ + Ns_DStringNAppend(dsPtr, toProcess, (int)(breakChar - toProcess)); + switch (*breakChar) { case '<': - Ns_DStringAppend(dsPtr, "<"); + Ns_DStringNAppend(dsPtr, "<", 4); break; case '>': - Ns_DStringAppend(dsPtr, ">"); + Ns_DStringNAppend(dsPtr, ">", 4); break; - case '&': - Ns_DStringAppend(dsPtr, "&"); + case '&': + Ns_DStringNAppend(dsPtr, "&", 5); break; - case '\'': - Ns_DStringAppend(dsPtr, "'"); - break; + case '\'': + Ns_DStringNAppend(dsPtr, "'", 5); + break; - case '"': - Ns_DStringAppend(dsPtr, """); - break; - - default: - Ns_DStringNAppend(dsPtr, htmlString, 1); + case '"': + Ns_DStringNAppend(dsPtr, """, 5); break; + + default: + /*should not happen */ assert(0); + break; + } + /* + * Check for further protected characters. + */ + toProcess = breakChar + 1; + breakChar = strpbrk(toProcess, "<>&'\""); + + } while (breakChar != NULL); + + /* + * Append the last part if non-empty. + */ + if (toProcess != NULL) { + Ns_DStringAppend(dsPtr, toProcess); + } +} + + +void +Ns_QuoteHtml(Ns_DString *dsPtr, const char *htmlString) +{ + NS_NONNULL_ASSERT(dsPtr != NULL); + NS_NONNULL_ASSERT(htmlString != NULL); + + /* + * If the first character is a null character, there is nothing to do. + */ + if (*htmlString != '\0') { + const char *breakChar = strpbrk(htmlString, "<>&'\""); + + if (breakChar != NULL) { + QuoteHtml(dsPtr, strpbrk(htmlString, "<>&'\""), htmlString); + } else { + Ns_DStringAppend(dsPtr, htmlString); } - ++htmlString; } } + /* *---------------------------------------------------------------------- * * NsTclQuoteHtmlObjCmd -- * - * Implements ns_quotehtml. + * Implements ns_quotehtml. * * Results: - * Tcl result. + * Tcl result. * * Side effects: - * See docs. + * See docs. * *---------------------------------------------------------------------- */ @@ -110,9 +158,9 @@ int NsTclQuoteHtmlObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) { int result = TCL_OK; - char *htmlString; + Tcl_Obj *htmlObj; Ns_ObjvSpec args[] = { - {"html", Ns_ObjvString, &htmlString, NULL}, + {"html", Ns_ObjvObj, &htmlObj, NULL}, {NULL, NULL, NULL, NULL} }; @@ -120,12 +168,25 @@ NsTclQuoteHtmlObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc result = TCL_ERROR; } else { - Ns_DString ds; + const char *htmlString = Tcl_GetString(htmlObj); - Ns_DStringInit(&ds); - Ns_QuoteHtml(&ds, htmlString); + if (*htmlString != '\0') { + const char *breakChar = strpbrk(htmlString, "<>&'\""); - Tcl_DStringResult(interp, &ds); + if (breakChar == NULL) { + /* + * No need to copy anything. + */ + Tcl_SetObjResult(interp, htmlObj); + } else { + Ns_DString ds; + + Ns_DStringInit(&ds); + QuoteHtml(&ds, breakChar, htmlString); + Tcl_DStringResult(interp, &ds); + + } + } } return result; diff --git a/nsd/return.c b/nsd/return.c index 0fef3367..97b4583b 100644 --- a/nsd/return.c +++ b/nsd/return.c @@ -1008,7 +1008,9 @@ ReturnRange(Ns_Conn *conn, const char *mimeType, (void) Ns_SetFileVec(bufs, 0, fd, data, 0, len); nbufs = 1; } - + /* + * Flush Headers and send file contents. + */ result = Ns_ConnWriteVData(conn, NULL, 0, NS_CONN_STREAM); if (result == NS_OK) { result = Ns_ConnSendFileVec(conn, bufs, nbufs); diff --git a/nsd/server.c b/nsd/server.c index 7ef830b1..f9a39cae 100644 --- a/nsd/server.c +++ b/nsd/server.c @@ -323,13 +323,13 @@ NsInitServer(const char *server, Ns_ServerInitProc *initProc) * NsRegisterServerInit -- * * Add a libnsd Ns_ServerInitProc to the end of the virtual server - * initialisation list. + * initialization list. * * Results: * None. * * Side effects: - * Proc will be called when virtual server is initialised. + * Proc will be called when virtual server is initialized. * *---------------------------------------------------------------------- */ diff --git a/nsd/sock.c b/nsd/sock.c index 271312c4..896d6e42 100644 --- a/nsd/sock.c +++ b/nsd/sock.c @@ -485,6 +485,7 @@ NS_SOCKET Ns_SockAccept(NS_SOCKET sock, struct sockaddr *saPtr, socklen_t *lenPtr) { sock = accept(sock, saPtr, lenPtr); + Ns_Log(Debug, "Ns_SockAccept returns sock %d, err %s", sock, (errno == 0) ? "NONE" : strerror(errno)); if (likely(sock != NS_INVALID_SOCKET)) { sock = SockSetup(sock); @@ -507,7 +508,7 @@ Ns_SockAccept(NS_SOCKET sock, struct sockaddr *saPtr, socklen_t *lenPtr) * A socket or NS_INVALID_SOCKET on error. * * Side effects: - * Will set SO_REUSEADDR always on the socket, SO_REUSEPORT + * Will set SO_REUSEADDR always on the socket, SO_REUSEPORT * optionally. * *---------------------------------------------------------------------- @@ -525,11 +526,12 @@ Ns_SockBind(const struct sockaddr *saPtr, bool reusePort) NS_SOCKET sock; NS_NONNULL_ASSERT(saPtr != NULL); + Ns_LogSockaddr(Debug, "Ns_SockBind called with", (const struct sockaddr *) saPtr); sock = (NS_SOCKET)socket((int)saPtr->sa_family, SOCK_STREAM, 0); if (sock != NS_INVALID_SOCKET) { - + #if defined(SO_REUSEPORT) if (reusePort) { int optval = 1; @@ -538,9 +540,11 @@ Ns_SockBind(const struct sockaddr *saPtr, bool reusePort) #endif sock = SockSetup(sock); } + if (sock != NS_INVALID_SOCKET) { + unsigned short port = Ns_SockaddrGetPort((const struct sockaddr *)saPtr); - if (Ns_SockaddrGetPort((const struct sockaddr *)saPtr) != 0u) { + if (port != 0u) { int n = 1; setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const void *) &n, (socklen_t)sizeof(n)); @@ -555,14 +559,26 @@ Ns_SockBind(const struct sockaddr *saPtr, bool reusePort) setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const void *) &n, (socklen_t)sizeof(n)); #endif } + Ns_LogSockaddr(Debug, "trying to bind on", (const struct sockaddr *) saPtr); if (bind(sock, (const struct sockaddr *)saPtr, Ns_SockaddrGetSockLen((const struct sockaddr *)saPtr)) != 0) { + Ns_Log(Notice, "bind operation on sock %d lead to error: %s", sock, ns_sockstrerror(ns_sockerrno)); Ns_LogSockaddr(Warning, "bind on", (const struct sockaddr *) saPtr); ns_sockclose(sock); sock = NS_INVALID_SOCKET; } + + if (port == 0u) { + /* + * Refetch the socket structure containing the potentially fresh port + */ + socklen_t socklen = Ns_SockaddrGetSockLen((const struct sockaddr *)saPtr); + + (void) getsockname(sock, (struct sockaddr *)saPtr, &socklen); + } + } return sock; @@ -678,13 +694,13 @@ Ns_SockTimedConnect2(const char *host, unsigned short port, const char *lhost, u sock = SockConnect(host, port, lhost, lport, NS_TRUE); if (sock != NS_INVALID_SOCKET) { Ns_ReturnCode status; - + status = Ns_SockTimedWait(sock, (unsigned int)NS_SOCK_WRITE, timeoutPtr); switch (status) { case NS_OK: { int err; - + len = (socklen_t)sizeof(err); if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (void *)&err, &len) == -1) { status = NS_ERROR; @@ -694,12 +710,12 @@ Ns_SockTimedConnect2(const char *host, unsigned short port, const char *lhost, u case NS_TIMEOUT: errno = ETIMEDOUT; break; - + case NS_ERROR: /* fall through */ case NS_FILTER_BREAK: /* fall through */ case NS_FILTER_RETURN: /* fall through */ case NS_FORBIDDEN: /* fall through */ - case NS_UNAUTHORIZED: + case NS_UNAUTHORIZED: break; } if (status != NS_OK) { @@ -732,7 +748,7 @@ Ns_ReturnCode Ns_SockSetNonBlocking(NS_SOCKET sock) { Ns_ReturnCode status; - + if (ns_sock_set_blocking(sock, NS_FALSE) == -1) { status = NS_ERROR; } else { @@ -762,7 +778,7 @@ Ns_ReturnCode Ns_SockSetBlocking(NS_SOCKET sock) { Ns_ReturnCode status; - + if (ns_sock_set_blocking(sock, NS_TRUE) == -1) { status = NS_ERROR; } else { @@ -802,7 +818,7 @@ Ns_SockSetDeferAccept(NS_SOCKET sock, long secs) # else int qlen = 5; # endif - + if (setsockopt(sock, IPPROTO_TCP, TCP_FASTOPEN, (const void *)&qlen, (socklen_t)sizeof(qlen)) == -1) { Ns_Log(Error, "deferaccept setsockopt(TCP_FASTOPEN): %s", @@ -863,7 +879,7 @@ Ns_ReturnCode Ns_SockPipe(NS_SOCKET socks[2]) { Ns_ReturnCode status; - + NS_NONNULL_ASSERT(socks != NULL); if (ns_sockpair(socks) != 0) { @@ -1072,7 +1088,7 @@ SockConnect(const char *host, unsigned short port, const char *lhost, unsigned s if (result != NS_OK) { Ns_Log(Debug, "SockConnect %s %d (local %s %d) fails", host, port, lhost, lport); sock = NS_INVALID_SOCKET; - + } else { sock = Ns_SockBind(lsaPtr, NS_FALSE); if (sock != NS_INVALID_SOCKET) { @@ -1084,7 +1100,7 @@ SockConnect(const char *host, unsigned short port, const char *lhost, unsigned s if (connect(sock, saPtr, Ns_SockaddrGetSockLen(saPtr)) != 0) { ns_sockerrno_t err = ns_sockerrno; - + if (!async || (err != NS_EINPROGRESS && err != NS_EWOULDBLOCK)) { ns_sockclose(sock); Ns_LogSockaddr(Warning, "SockConnect fails", saPtr); diff --git a/nsd/sockaddr.c b/nsd/sockaddr.c index a3b1d215..5c2fa146 100644 --- a/nsd/sockaddr.c +++ b/nsd/sockaddr.c @@ -274,6 +274,34 @@ ns_inet_ntop(const struct sockaddr *saPtr, char *buffer, size_t size) { if (saPtr->sa_family == AF_INET6) { result = inet_ntop(AF_INET6, &((const struct sockaddr_in6 *)saPtr)->sin6_addr, buffer, (socklen_t)size); + + if (result != NULL) { + const struct in6_addr *addr = &(((struct sockaddr_in6 *)saPtr)->sin6_addr); + /* + * In case the address is V4MAPPED, return just the IPv4 portion, + * since ns_inet_pton() tries to return as well first an IPv4 + * address. This is important, since getsockname() might return + * for a socket AF_INET6, although the socket was created with + * AF_INET (see e.g. ListenCallback() in listen.c). + */ + if (IN6_IS_ADDR_V4MAPPED(addr)) { + const char *tail = strrchr(result, INTCHAR(':')); + + /* + * When the last ':' in the converted string is further away + * from the end as possible with an pure IPv6 notation, then + * assume the last portion is an IPv4 address. + */ + if (tail != NULL) { + size_t len = strlen(tail); + + if (len > 6) { + tail ++; + memcpy(buffer, tail, len); + } + } + } + } } else { result = inet_ntop(AF_INET, &((const struct sockaddr_in *)saPtr)->sin_addr, buffer, (socklen_t)size); } @@ -409,6 +437,7 @@ Ns_GetSockAddr(struct sockaddr *saPtr, const char *host, unsigned short port) return status; } + /* *---------------------------------------------------------------------- * @@ -476,6 +505,7 @@ Ns_SockaddrSetPort(struct sockaddr *saPtr, unsigned short port) } + /* *---------------------------------------------------------------------- * @@ -507,6 +537,7 @@ Ns_SockaddrGetSockLen(const struct sockaddr *saPtr) return (socklen_t)socklen; } + /* *---------------------------------------------------------------------- * diff --git a/nsd/tclcache.c b/nsd/tclcache.c index 9950faa8..bceea988 100644 --- a/nsd/tclcache.c +++ b/nsd/tclcache.c @@ -53,24 +53,86 @@ typedef struct TclCache { * Local functions defined in this file */ -static int CacheAppendObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv, int append); +static int CacheAppendObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv, bool append); static Ns_Entry *CreateEntry(const NsInterp *itPtr, TclCache *cPtr, const char *key, - int *newPtr, Ns_Time *timeoutPtr) + int *newPtr, Ns_Time *timeoutPtr, const Ns_CacheTransactionStack *transactionStackPtr) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(3) NS_GNUC_NONNULL(4); -static void SetEntry(TclCache *cPtr, Ns_Entry *entry, Tcl_Obj *valObj, Ns_Time *expPtr, int cost) - NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(3); +static void SetEntry(NsInterp *itPtr, TclCache *cPtr, Ns_Entry *entry, Tcl_Obj *valObj, Ns_Time *expPtr, int cost) + NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(3) NS_GNUC_NONNULL(4); -static bool noGlobChars(const char *pattern) +static bool noGlobChars(const char *pattern) NS_GNUC_NONNULL(1); static TclCache *TclCacheCreate(const char *name, size_t maxEntry, size_t maxSize, Ns_Time *timeoutPtr, Ns_Time *expPtr) NS_GNUC_NONNULL(1) NS_GNUC_RETURNS_NONNULL; +static Tcl_Obj*GetCacheNames(NsServer *servPtr, bool withUncommittedEntries) + NS_GNUC_NONNULL(1) NS_GNUC_RETURNS_NONNULL; + +static int +CacheTransactionFinishObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv, bool commit); + +static int +CacheTransactionFinish(NsServer *servPtr, const char *cacheName, uintptr_t transactionEpoch, bool commit, unsigned long *countPtr) + NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(5); + +static int +CacheTransactionFinishPop(NsInterp *itPtr, Tcl_Obj *listObj, bool commit, unsigned long *countPtr) + NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(4); + + static Ns_ObjvProc ObjvCache; + +/* + *---------------------------------------------------------------------- + * + * GetCacheNames -- + * + * Return a Tcl list of Tcl caches. + * + * Results: + * Tcl_Obj * + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static Tcl_Obj* +GetCacheNames(NsServer *servPtr, bool withUncommittedEntries) { + const Tcl_HashEntry *hPtr; + Tcl_HashSearch search; + Tcl_Obj *listObj = Tcl_NewListObj(0, NULL); + + NS_NONNULL_ASSERT(servPtr != NULL); + + Ns_MutexLock(&servPtr->tcl.cachelock); + for (hPtr = Tcl_FirstHashEntry(&servPtr->tcl.caches, &search); + hPtr != NULL; + hPtr = Tcl_NextHashEntry(&search) + ) { + const char *key = Tcl_GetHashKey(&servPtr->tcl.caches, hPtr); + + if (withUncommittedEntries) { + const TclCache *cPtr = Tcl_GetHashValue(hPtr); + + if (Ns_CacheGetNrUncommittedEntries(cPtr->cache) > 0) { + Tcl_ListObjAppendElement(NULL, listObj, Tcl_NewStringObj(key, -1)); + } + } else { + Tcl_ListObjAppendElement(NULL, listObj, Tcl_NewStringObj(key, -1)); + } + } + Ns_MutexUnlock(&servPtr->tcl.cachelock); + + return listObj; +} + /* *---------------------------------------------------------------------- @@ -148,7 +210,7 @@ NsTclCacheCreateObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_ }; if (Ns_ParseObjv(opts, args, interp, 1, objc, objv) != NS_OK) { result = TCL_ERROR; - + } else if (maxSize < 0 || maxEntry < 0) { Ns_TclPrintfResult(interp, "maxsize and maxentry must be positive numbers"); result = TCL_ERROR; @@ -157,7 +219,7 @@ NsTclCacheCreateObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_ const NsInterp *itPtr = clientData; NsServer *servPtr = itPtr->servPtr; Tcl_HashEntry *hPtr; - int isNew; + int isNew; Ns_MutexLock(&servPtr->tcl.cachelock); hPtr = Tcl_CreateHashEntry(&servPtr->tcl.caches, name, &isNew); @@ -169,7 +231,7 @@ NsTclCacheCreateObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_ Tcl_SetObjResult(interp, Tcl_NewBooleanObj( isNew == 1)); } - + return result; } @@ -226,7 +288,7 @@ NsTclCacheExistsObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_ * NsTclCacheConfigureObjCmd -- * * Configure a Tcl cache. Usage: - * ns_cache_configure /cache/ ?-timeout T1? ?-expires T2? ?-maxentry E? ?-maxsize S? + * ns_cache_configure /cache/ ?-timeout T1? ?-expires T2? ?-maxentry E? ?-maxsize S? * * Results: * Tcl result. @@ -248,7 +310,7 @@ NsTclCacheConfigureObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, T {"-timeout", Ns_ObjvTime, &timeoutPtr, NULL}, {"-expires", Ns_ObjvTime, &expPtr, NULL}, {"-maxentry", Ns_ObjvLong, &maxEntry, NULL}, - {"-maxsize", Ns_ObjvLong, &maxSize, NULL}, + {"-maxsize", Ns_ObjvLong, &maxSize, NULL}, {NULL, NULL, NULL, NULL} }; Ns_ObjvSpec args[] = { @@ -266,7 +328,7 @@ NsTclCacheConfigureObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, T } else if (objc > 2 && Ns_ParseObjv(opts, NULL, interp, 2, objc, objv) != NS_OK) { result = TCL_ERROR; - + } else if (maxSize < 0) { Ns_TclPrintfResult(interp, "maxsize must be a positive number"); result = TCL_ERROR; @@ -274,11 +336,13 @@ NsTclCacheConfigureObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, T } else if (maxEntry < 0) { Ns_TclPrintfResult(interp, "maxEntry must be a positive number"); result = TCL_ERROR; - + } else if (objc > 2) { const NsInterp *itPtr = clientData; NsServer *servPtr = itPtr->servPtr; + assert(cPtr != NULL); + Ns_MutexLock(&servPtr->tcl.cachelock); if (maxEntry > 0) { cPtr->maxEntry = (size_t)maxEntry; @@ -300,6 +364,8 @@ NsTclCacheConfigureObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, T Tcl_Obj *resultObj = Tcl_NewListObj(0, NULL); Tcl_DString ds; + assert(cPtr != NULL); + Tcl_DStringInit(&ds); Ns_MutexLock(&servPtr->tcl.cachelock); @@ -312,16 +378,16 @@ NsTclCacheConfigureObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, T Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("maxentry", 8)); Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewLongObj(maxEntry)); - + Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("expires", 7)); if (cPtr->expires.sec != 0 || cPtr->expires.usec != 0) { Ns_DStringAppendTime(&ds, &cPtr->expires); Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj(ds.string, ds.length)); - Tcl_DStringTrunc(&ds, 0); + Tcl_DStringSetLength(&ds, 0); } else { Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("", 0)); } - + Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("timeout", 7)); if (cPtr->timeout.sec != 0 || cPtr->timeout.usec != 0) { Ns_DStringAppendTime(&ds, &cPtr->timeout); @@ -363,11 +429,11 @@ NsTclCacheConfigureObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, T int NsTclCacheEvalObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) { - const NsInterp *itPtr = clientData; - TclCache *cPtr = NULL; - char *key; - Ns_Time *timeoutPtr = NULL, *expPtr = NULL; - int nargs = 0, isNew, force = (int)NS_FALSE, status; + NsInterp *itPtr = clientData; + TclCache *cPtr = NULL; + char *key; + Ns_Time *timeoutPtr = NULL, *expPtr = NULL; + int nargs = 0, isNew, force = (int)NS_FALSE, status; Ns_ObjvSpec opts[] = { {"-timeout", Ns_ObjvTime, &timeoutPtr, NULL}, @@ -387,24 +453,44 @@ NsTclCacheEvalObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Ob status = TCL_ERROR; } else { - Ns_Entry *entry = CreateEntry(itPtr, cPtr, key, &isNew, timeoutPtr); + Ns_Entry *entry; + Ns_CacheTransactionStack *transactionStackPtr = &itPtr->cacheTransactionStack; + + assert(cPtr != NULL); + assert(key != NULL); + + /* + * CreateEntry waits for ongoing transactions. If it succeeds, it + * provides either a fresh entry (isNew == 1) or an entry with a + * provided cache value (isNew == 0) ... which might be from the + * current transaction. + */ + entry = CreateEntry(itPtr, cPtr, key, &isNew, timeoutPtr, transactionStackPtr); if (unlikely(entry == NULL)) { status = TCL_ERROR; - + } else if (likely(isNew == 0 && force == 0)) { - Tcl_Obj *resultObj = Tcl_NewStringObj(Ns_CacheGetValue(entry), - (int)Ns_CacheGetSize(entry)); + char *value = Ns_CacheGetValueT(entry, transactionStackPtr); + Tcl_Obj *resultObj = Tcl_NewStringObj(value, (int)Ns_CacheGetSize(entry)); + + /* + * We have a value for the cache entry, return it. + */ Ns_CacheUnlock(cPtr->cache); Tcl_SetObjResult(interp, resultObj); status = TCL_OK; } else { Ns_Time start, end, diff; - + + /* + * Evaluate the cmd to obtain the cache value. + */ + Ns_CacheUnlock(cPtr->cache); Ns_GetTime(&start); - + if (nargs == 1) { status = Tcl_EvalObjEx(interp, objv[objc-1], 0); } else { @@ -412,13 +498,13 @@ NsTclCacheEvalObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Ob } Ns_GetTime(&end); (void)Ns_DiffTime(&end, &start, &diff); - + if (status != TCL_OK && status != TCL_RETURN) { - /* + /* * Don't cache anything, if the status code is not TCL_OK * or TCL_RETURN. - * + * * The remaining status codes are TCL_BREAK, TCL_CONTINUE * and TCL_ERROR. Regarding TCL_BREAK and TCL_CONTINUE as * signals for not cacheing is used e.g. in @@ -436,10 +522,10 @@ NsTclCacheEvalObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Ob Ns_CacheDeleteEntry(entry); } else { Tcl_Obj *resultObj = Tcl_GetObjResult(interp); - + status = TCL_OK; Ns_CacheLock(cPtr->cache); - SetEntry(cPtr, entry, resultObj, expPtr, + SetEntry(itPtr, cPtr, entry, resultObj, expPtr, (int)(diff.sec * 1000000 + diff.usec)); } Ns_CacheBroadcast(cPtr->cache); @@ -470,11 +556,11 @@ NsTclCacheEvalObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Ob int NsTclCacheIncrObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) { - const NsInterp *itPtr = clientData; - TclCache *cPtr; - char *key; - int isNew, incr = 1, result; - Ns_Time *timeoutPtr = NULL, *expPtr = NULL; + NsInterp *itPtr = clientData; + TclCache *cPtr; + char *key; + int isNew, incr = 1, result; + Ns_Time *timeoutPtr = NULL, *expPtr = NULL; Ns_ObjvSpec opts[] = { {"-timeout", Ns_ObjvTime, &timeoutPtr, NULL}, @@ -493,19 +579,20 @@ NsTclCacheIncrObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Ob if (Ns_ParseObjv(opts, args, interp, 1, objc, objv) != NS_OK) { result = TCL_ERROR; } else { - Ns_Entry *entry = CreateEntry(itPtr, cPtr, key, &isNew, timeoutPtr); + Ns_CacheTransactionStack *transactionStackPtr = &itPtr->cacheTransactionStack; + Ns_Entry *entry = CreateEntry(itPtr, cPtr, key, &isNew, timeoutPtr, transactionStackPtr); int cur = 0; - + if (entry == NULL) { result = TCL_ERROR; } else if ((isNew == 0) - && (Tcl_GetInt(interp, Ns_CacheGetValue(entry), &cur) != TCL_OK)) { + && (Tcl_GetInt(interp, Ns_CacheGetValueT(entry, transactionStackPtr), &cur) != TCL_OK)) { Ns_CacheUnlock(cPtr->cache); result = TCL_ERROR; } else { - Tcl_Obj *valObj = Tcl_NewIntObj(cur + incr); - - SetEntry(cPtr, entry, valObj, expPtr, 0); + Tcl_Obj *valObj = Tcl_NewIntObj(cur + incr); + + SetEntry(itPtr, cPtr, entry, valObj, expPtr, 0); Tcl_SetObjResult(interp, valObj); Ns_CacheUnlock(cPtr->cache); result = TCL_OK; @@ -535,23 +622,23 @@ NsTclCacheIncrObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Ob int NsTclCacheAppendObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) { - return CacheAppendObjCmd(clientData, interp, objc, objv, 1); + return CacheAppendObjCmd(clientData, interp, objc, objv, NS_TRUE); } int NsTclCacheLappendObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) { - return CacheAppendObjCmd(clientData, interp, objc, objv, 0); + return CacheAppendObjCmd(clientData, interp, objc, objv, NS_FALSE); } static int -CacheAppendObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv, int append) +CacheAppendObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv, bool append) { - const NsInterp *itPtr = clientData; - TclCache *cPtr = NULL; - char *key = NULL; - int result = TCL_OK, nelements = 0; - Ns_Time *timeoutPtr = NULL, *expPtr = NULL; + NsInterp *itPtr = clientData; + TclCache *cPtr = NULL; + char *key = NULL; + int result = TCL_OK, nelements = 0; + Ns_Time *timeoutPtr = NULL, *expPtr = NULL; Ns_ObjvSpec opts[] = { {"-timeout", Ns_ObjvTime, &timeoutPtr, NULL}, @@ -571,21 +658,26 @@ CacheAppendObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj * result = TCL_ERROR; } else { - int isNew; - Ns_Entry *entry = CreateEntry(itPtr, cPtr, key, &isNew, timeoutPtr); - + int isNew; + Ns_Entry *entry; + Ns_CacheTransactionStack *transactionStackPtr = &itPtr->cacheTransactionStack; + + assert(cPtr != NULL); + assert(key != NULL); + + entry = CreateEntry(itPtr, cPtr, key, &isNew, timeoutPtr, transactionStackPtr); if (entry == NULL) { result = TCL_ERROR; } else { Tcl_Obj *valObj = Tcl_NewObj(); int i; - + if (isNew == 0) { - Tcl_SetStringObj(valObj, Ns_CacheGetValue(entry), + Tcl_SetStringObj(valObj, Ns_CacheGetValueT(entry, transactionStackPtr), (int)Ns_CacheGetSize(entry)); } for (i = objc - nelements; i < objc; i++) { - if (append != 0) { + if (append) { Tcl_AppendObjToObj(valObj, objv[i]); } else if (Tcl_ListObjAppendElement(interp, valObj, objv[i]) != TCL_OK) { result = TCL_ERROR; @@ -593,7 +685,7 @@ CacheAppendObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj * } } if (result == TCL_OK) { - SetEntry(cPtr, entry, valObj, expPtr, 0); + SetEntry(itPtr, cPtr, entry, valObj, expPtr, 0); Tcl_SetObjResult(interp, valObj); } Ns_CacheUnlock(cPtr->cache); @@ -624,21 +716,8 @@ NsTclCacheNamesObjCmd(ClientData clientData, Tcl_Interp *interp, int UNUSED(objc { const NsInterp *itPtr = clientData; NsServer *servPtr = itPtr->servPtr; - const Tcl_HashEntry *hPtr; - Tcl_HashSearch search; - Tcl_Obj *listObj = Tcl_NewListObj(0, NULL); - - Ns_MutexLock(&servPtr->tcl.cachelock); - for (hPtr = Tcl_FirstHashEntry(&servPtr->tcl.caches, &search); - hPtr != NULL; - hPtr = Tcl_NextHashEntry(&search) - ) { - const char *key = Tcl_GetHashKey(&servPtr->tcl.caches, hPtr); - Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(key, -1)); - } - Ns_MutexUnlock(&servPtr->tcl.cachelock); - Tcl_SetObjResult(interp, listObj); + Tcl_SetObjResult(interp, GetCacheNames(servPtr, NS_FALSE)); return TCL_OK; } @@ -660,7 +739,7 @@ NsTclCacheNamesObjCmd(ClientData clientData, Tcl_Interp *interp, int UNUSED(objc *---------------------------------------------------------------------- */ static bool -noGlobChars(const char *pattern) +noGlobChars(const char *pattern) { register char c; const char *p = pattern; @@ -669,7 +748,7 @@ noGlobChars(const char *pattern) NS_NONNULL_ASSERT(pattern != NULL); for (c = *p; likely(c != '\0'); c = *++p) { - if (unlikely(c == '*') || unlikely(c == '?') || unlikely(c == '[')) { + if (unlikely(c == '*') || unlikely(c == '?') || unlikely(c == '[')) { result = NS_FALSE; break; } @@ -681,10 +760,12 @@ noGlobChars(const char *pattern) int NsTclCacheKeysObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) { - TclCache *cPtr = NULL; - const Ns_Entry *entry; - char *pattern = NULL; - int exact = (int)NS_FALSE, result = TCL_OK; + TclCache *cPtr = NULL; + const Ns_Entry *entry; + char *pattern = NULL; + int exact = (int)NS_FALSE, result = TCL_OK; + const NsInterp *itPtr = clientData; + const Ns_CacheTransactionStack *transactionStackPtr = &itPtr->cacheTransactionStack; Ns_ObjvSpec opts[] = { {"-exact", Ns_ObjvBool, &exact, INT2PTR(NS_TRUE)}, @@ -712,17 +793,17 @@ NsTclCacheKeysObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Ob */ assert(cPtr != NULL); Ns_CacheLock(cPtr->cache); - entry = Ns_CacheFindEntry(cPtr->cache, pattern); - if (entry != NULL && Ns_CacheGetValue(entry) != NULL) { + entry = Ns_CacheFindEntryT(cPtr->cache, pattern, transactionStackPtr); + if (entry != NULL && Ns_CacheGetValueT(entry, transactionStackPtr) != NULL) { Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(pattern, -1)); } Ns_CacheUnlock(cPtr->cache); Tcl_SetObjResult(interp, listObj); - + } else { Ns_CacheSearch search; Tcl_Obj *listObj = Tcl_NewListObj(0, NULL); - + /* * We have either no pattern or the pattern contains meta * characters. We need to iterate over all entries, which can @@ -730,19 +811,19 @@ NsTclCacheKeysObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Ob */ assert(cPtr != NULL); Ns_CacheLock(cPtr->cache); - entry = Ns_CacheFirstEntry(cPtr->cache, &search); + entry = Ns_CacheFirstEntryT(cPtr->cache, &search, transactionStackPtr); while (entry != NULL) { const char *key = Ns_CacheKey(entry); if (pattern == NULL || Tcl_StringMatch(key, pattern) == 1) { Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(key, -1)); } - entry = Ns_CacheNextEntry(&search); + entry = Ns_CacheNextEntryT(&search, transactionStackPtr); } Ns_CacheUnlock(cPtr->cache); Tcl_SetObjResult(interp, listObj); } - + return result; } @@ -769,8 +850,11 @@ NsTclCacheKeysObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Ob int NsTclCacheFlushObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) { - TclCache *cPtr = NULL; - int glob = (int)NS_FALSE, npatterns = 0, result = TCL_OK; + TclCache *cPtr = NULL; + int glob = (int)NS_FALSE, npatterns = 0, result = TCL_OK; + const NsInterp *itPtr = clientData; + const Ns_CacheTransactionStack *transactionStackPtr = &itPtr->cacheTransactionStack; + Ns_ObjvSpec opts[] = { {"-glob", Ns_ObjvBool, &glob, INT2PTR(NS_TRUE)}, {"--", Ns_ObjvBreak, NULL, NULL}, @@ -785,6 +869,7 @@ NsTclCacheFlushObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_O if (Ns_ParseObjv(opts, args, interp, 1, objc, objv) != NS_OK) { result = TCL_ERROR; + } else { Ns_Entry *entry; int nflushed = 0, i; @@ -799,17 +884,17 @@ NsTclCacheFlushObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_O } else if (glob == 0) { for (i = npatterns; i > 0; i--) { - entry = Ns_CacheFindEntry(cache, Tcl_GetString(objv[objc-i])); - if (entry != NULL && Ns_CacheGetValue(entry) != NULL) { + entry = Ns_CacheFindEntryT(cache, Tcl_GetString(objv[objc-i]), transactionStackPtr); + if (entry != NULL && Ns_CacheGetValueT(entry, transactionStackPtr) != NULL) { Ns_CacheFlushEntry(entry); nflushed++; } } - + } else { Ns_CacheSearch search; - - entry = Ns_CacheFirstEntry(cache, &search); + + entry = Ns_CacheFirstEntryT(cache, &search, transactionStackPtr); while (entry != NULL) { const char *key = Ns_CacheKey(entry); @@ -822,7 +907,7 @@ NsTclCacheFlushObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_O break; } } - entry = Ns_CacheNextEntry(&search); + entry = Ns_CacheNextEntryT(&search, transactionStackPtr); } } Ns_CacheUnlock(cache); @@ -869,14 +954,26 @@ NsTclCacheGetObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj result = TCL_ERROR; } else { - const Ns_Entry *entry; - Tcl_Obj *resultObj; - + const Ns_Entry *entry; + Tcl_Obj *resultObj; + const NsInterp *itPtr = clientData; + const Ns_CacheTransactionStack *transactionStackPtr = &itPtr->cacheTransactionStack; + assert(cPtr != NULL); Ns_CacheLock(cPtr->cache); - entry = Ns_CacheFindEntry(cPtr->cache, key); - resultObj = (entry != NULL) ? Tcl_NewStringObj(Ns_CacheGetValue(entry), -1) : NULL; + entry = Ns_CacheFindEntryT(cPtr->cache, key, transactionStackPtr); + if (entry != NULL) { + void *value = Ns_CacheGetValueT(entry, transactionStackPtr); + + if (value != NULL) { + resultObj = Tcl_NewStringObj(value, -1); + } else { + resultObj = NULL; + } + } else { + resultObj = NULL; + } Ns_CacheUnlock(cPtr->cache); if (unlikely(varNameObj != NULL)) { @@ -936,13 +1033,13 @@ NsTclCacheStatsObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_O if (Ns_ParseObjv(opts, args, interp, 1, objc, objv) != NS_OK) { result = TCL_ERROR; - + } else { Ns_DString ds; Ns_Cache *cache; assert(cPtr != NULL); - + cache = cPtr->cache; Ns_DStringInit(&ds); @@ -977,7 +1074,7 @@ NsTclCacheStatsObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_O Tcl_DStringResult(interp, &ds); } - + return result; } @@ -1002,7 +1099,7 @@ NsTclCacheStatsObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_O static Ns_Entry * CreateEntry(const NsInterp *itPtr, TclCache *cPtr, const char *key, int *newPtr, - Ns_Time *timeoutPtr) + Ns_Time *timeoutPtr, const Ns_CacheTransactionStack *transactionStackPtr) { Ns_Cache *cache; Ns_Entry *entry; @@ -1022,7 +1119,7 @@ CreateEntry(const NsInterp *itPtr, TclCache *cPtr, const char *key, int *newPtr, timeoutPtr = Ns_AbsoluteTime(&t, timeoutPtr); } Ns_CacheLock(cache); - entry = Ns_CacheWaitCreateEntry(cache, key, newPtr, timeoutPtr); + entry = Ns_CacheWaitCreateEntryT(cache, key, newPtr, timeoutPtr, transactionStackPtr); if (entry == NULL) { Ns_CacheUnlock(cache); Tcl_SetErrorCode(itPtr->interp, "NS_TIMEOUT", (char *)0L); @@ -1040,7 +1137,7 @@ CreateEntry(const NsInterp *itPtr, TclCache *cPtr, const char *key, int *newPtr, * Set the value of the cache entry if not above max entry size. * * Results: - * 1 if entry set, 0 otherwise. + * None. * * Side effects: * None. @@ -1049,12 +1146,11 @@ CreateEntry(const NsInterp *itPtr, TclCache *cPtr, const char *key, int *newPtr, */ static void -SetEntry(TclCache *cPtr, Ns_Entry *entry, Tcl_Obj *valObj, Ns_Time *expPtr, int cost) +SetEntry(NsInterp *itPtr, TclCache *cPtr, Ns_Entry *entry, Tcl_Obj *valObj, Ns_Time *expPtr, int cost) { const char *bytes; int len; size_t length; - Ns_Time t; NS_NONNULL_ASSERT(cPtr != NULL); NS_NONNULL_ASSERT(entry != NULL); @@ -1067,7 +1163,9 @@ SetEntry(TclCache *cPtr, Ns_Entry *entry, Tcl_Obj *valObj, Ns_Time *expPtr, int if (cPtr->maxEntry > 0u && length > cPtr->maxEntry) { Ns_CacheDeleteEntry(entry); } else { - char *value = ns_malloc(length + 1u); + Ns_CacheTransactionStack *transactionStackPtr = &itPtr->cacheTransactionStack; + char *value = ns_malloc(length + 1u); + Ns_Time t; memcpy(value, bytes, length); value[length] = '\0'; @@ -1077,7 +1175,14 @@ SetEntry(TclCache *cPtr, Ns_Entry *entry, Tcl_Obj *valObj, Ns_Time *expPtr, int } else { expPtr = Ns_AbsoluteTime(&t, expPtr); } - Ns_CacheSetValueExpires(entry, value, length, expPtr, cost, cPtr->maxSize); + if (transactionStackPtr->depth > 0) { + int uncommitted = Ns_CacheSetValueExpires(entry, value, length, expPtr, cost, cPtr->maxSize, + transactionStackPtr->stack[transactionStackPtr->depth - 1]); + transactionStackPtr->uncommitted[transactionStackPtr->depth - 1] += uncommitted; + } else { + (void) Ns_CacheSetValueExpires(entry, value, length, expPtr, cost, cPtr->maxSize, 0u); + } + } } @@ -1099,11 +1204,10 @@ SetEntry(TclCache *cPtr, Ns_Entry *entry, Tcl_Obj *valObj, Ns_Time *expPtr, int */ static int -ObjvCache(Ns_ObjvSpec *spec, Tcl_Interp *interp, int *objcPtr, - Tcl_Obj *CONST* objv) +ObjvCache(Ns_ObjvSpec *spec, Tcl_Interp *interp, int *objcPtr, Tcl_Obj *CONST* objv) { - int result = TCL_OK; - TclCache **cPtrPtr = spec->dest; + int result = TCL_OK; + TclCache **cPtrPtr = spec->dest; if (unlikely(*objcPtr < 1)) { result = TCL_ERROR; @@ -1112,7 +1216,7 @@ ObjvCache(Ns_ObjvSpec *spec, Tcl_Interp *interp, int *objcPtr, static const char *const cacheType = "ns:cache"; const NsInterp *itPtr = spec->arg; Tcl_Obj *cacheNameObj = objv[0]; - const char *cacheName = Tcl_GetString(cacheNameObj); + const char *cacheName = Tcl_GetString(cacheNameObj); if (unlikely(Ns_TclGetOpaqueFromObj(cacheNameObj, cacheType, (void **)cPtrPtr) != TCL_OK)) { /* @@ -1150,7 +1254,7 @@ ObjvCache(Ns_ObjvSpec *spec, Tcl_Interp *interp, int *objcPtr, * representation. */ } - + if (result == TCL_OK) { *objcPtr -= 1; } @@ -1159,6 +1263,267 @@ ObjvCache(Ns_ObjvSpec *spec, Tcl_Interp *interp, int *objcPtr, return result; } + +/* + *---------------------------------------------------------------------- + * + * NsTclCacheTransactionBeginObjCmd -- + * + * Create a cache transaction and return a new transaction ID. + * + * Results: + * Tcl result. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +NsTclCacheTransactionBeginObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) +{ + int result; + + if (Ns_ParseObjv(NULL, NULL, interp, 1, objc, objv) != NS_OK) { + result = TCL_ERROR; + + } else { + NsInterp *itPtr = clientData; + NsServer *servPtr = itPtr->servPtr; + uintptr_t transactionEpoch; + Ns_CacheTransactionStack *transactionStackPtr = &itPtr->cacheTransactionStack; + + Ns_MutexLock(&servPtr->tcl.cachelock); + transactionEpoch = ++servPtr->tcl.transactionEpoch; + Ns_MutexUnlock(&servPtr->tcl.cachelock); + + if (transactionStackPtr->depth < NS_CACHE_MAX_TRANSACTION_DEPTH) { + transactionStackPtr->stack[transactionStackPtr->depth] = transactionEpoch; + transactionStackPtr->uncommitted[transactionStackPtr->depth] = 0; + transactionStackPtr->depth++; + Tcl_SetObjResult(interp, Tcl_NewWideIntObj((Tcl_WideInt)transactionEpoch)); + result = TCL_OK; + + } else { + Ns_TclPrintfResult(interp, "max cache transaction depth exceeded"); + result = TCL_ERROR; + } + } + return result; +} + + +/* + *---------------------------------------------------------------------- + * + * NsTclCacheTransactionCommitObjCmd, NsTclCacheTransactionRollbackObjCmd -- + * + * End a cache transaction and commit or rollback cache. + * + * Results: + * Tcl result. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ +int +NsTclCacheTransactionCommitObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) +{ + return CacheTransactionFinishObjCmd(clientData, interp, objc, objv, NS_TRUE); +} + +int +NsTclCacheTransactionRollbackObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) +{ + return CacheTransactionFinishObjCmd(clientData, interp, objc, objv, NS_FALSE); +} + +static int +CacheTransactionFinishObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv, bool commit) +{ + int result, all = (int)NS_FALSE; + Ns_ObjvSpec opts[] = { + {"-all", Ns_ObjvBool, &all, INT2PTR(NS_TRUE)}, + {NULL, NULL, NULL, NULL} + }; + + if (Ns_ParseObjv(opts, NULL, interp, 1, objc, objv) != NS_OK) { + result = TCL_ERROR; + + } else { + NsInterp *itPtr = clientData; + Tcl_Obj *listObj; + unsigned long count = 0u; + Ns_Time startTime, endTime, diffTime; + + Ns_GetTime(&startTime); + + listObj = GetCacheNames(itPtr->servPtr, NS_TRUE); + Tcl_IncrRefCount(listObj); + + if (all == (int)NS_FALSE) { + /* + * Pop a single entry from the transaction stack. If there is + * none, complain. + */ + if (itPtr->cacheTransactionStack.depth > 0u) { + result = CacheTransactionFinishPop(itPtr, listObj, commit, &count); + if (result == TCL_OK) { + Tcl_SetObjResult(interp, Tcl_NewWideIntObj((Tcl_WideInt)count)); + } + } else { + Ns_TclPrintfResult(interp, "no cache transaction active"); + result = TCL_ERROR; + } + } else { + /* + * Unwind the whole transaction stack. If the stack is empty, be silent. + */ + + result = TCL_OK; + while (itPtr->cacheTransactionStack.depth > 0u) { + result = CacheTransactionFinishPop(itPtr, listObj, commit, &count); + if (result != TCL_OK) { + break; + } + } + } + Tcl_DecrRefCount(listObj); + + if (count > 0u) { + Ns_GetTime(&endTime); + Ns_DiffTime(&endTime, &startTime, &diffTime); + + Ns_Log(Notice, "CacheTransactionFinish: %s %lu elements in (%" PRIu64 ".%06ld secs)", + commit ? "committed" : "rolled back", + count, (int64_t)diffTime.sec, diffTime.usec); + } + } + return result; +} + + + +/* + *---------------------------------------------------------------------- + * + * CacheTransactionFinishPop -- + * + * Finish a single cache transaction on a single cache and perform either + * a commit or rollback. + * + * Results: + * Tcl result. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ +static int +CacheTransactionFinish(NsServer *servPtr, const char *cacheName, uintptr_t transactionEpoch, bool commit, unsigned long *countPtr) +{ + const Tcl_HashEntry *hPtr; + int result; + + NS_NONNULL_ASSERT(servPtr != NULL); + NS_NONNULL_ASSERT(cacheName != NULL); + NS_NONNULL_ASSERT(countPtr != NULL); + + Ns_MutexLock(&servPtr->tcl.cachelock); + hPtr = Tcl_FindHashEntry(&servPtr->tcl.caches, (const void *)cacheName); + Ns_MutexUnlock(&servPtr->tcl.cachelock); + + if (unlikely(hPtr == NULL)) { + result = TCL_ERROR; + + } else { + TclCache *cPtr = Tcl_GetHashValue(hPtr); + Ns_Cache *cache; + + assert(cPtr != NULL); + cache = cPtr->cache; + + Ns_CacheLock(cache); + if (commit) { + *countPtr += Ns_CacheCommitEntries(cache, transactionEpoch); + } else { + *countPtr += Ns_CacheRollbackEntries(cache, transactionEpoch); + } + Ns_CacheUnlock(cache); + result = TCL_OK; + } + + return result; +} + + +/* + *---------------------------------------------------------------------- + * + * CacheTransactionFinishPop -- + * + * Pop a single cache transaction from the transaction stack and either + * commit or rollback on all caches. + * + * Results: + * Tcl result. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ +static int +CacheTransactionFinishPop(NsInterp *itPtr, Tcl_Obj *listObj, bool commit, unsigned long *countPtr) +{ + uintptr_t transactionEpoch; + int result = TCL_OK; + Ns_CacheTransactionStack *transactionStackPtr = &itPtr->cacheTransactionStack; + + NS_NONNULL_ASSERT(itPtr != NULL); + NS_NONNULL_ASSERT(listObj != NULL); + NS_NONNULL_ASSERT(countPtr != NULL); + + /*for (i = 0u; i < transactionStackPtr->depth; i++) { + fprintf(stderr, "transaction stack [%u]: epoch %" PRIuPTR " uncommitted %d\n", + i, transactionStackPtr->stack[i], transactionStackPtr->uncommitted[i]); + }*/ + transactionEpoch = transactionStackPtr->stack[--transactionStackPtr->depth]; + transactionStackPtr->stack[transactionStackPtr->depth] = 0u; + + /* + * Only iterate over the caches, when there are uncommitted entries for + * this epoch. + */ + + if (transactionStackPtr->uncommitted[transactionStackPtr->depth] > 0) { + Tcl_Obj **lobjv; + int lobjc; + unsigned int i; + + Tcl_ListObjGetElements(itPtr->interp, listObj, &lobjc, &lobjv); + for (i = 0u; i < (unsigned int)lobjc; i++) { + const char *cacheName = Tcl_GetString(lobjv[i]); + + result = CacheTransactionFinish(itPtr->servPtr, cacheName, transactionEpoch, commit, countPtr); + if (result != TCL_OK) { + + Ns_TclPrintfResult(itPtr->interp, "no such cache: %s", cacheName); + Tcl_SetErrorCode(itPtr->interp, "NSCACHE", "LOOKUP", cacheName, NULL); + break; + } + } + } + + return result; +} + + + /* * Local Variables: * mode: c diff --git a/nsd/tclcmds.c b/nsd/tclcmds.c index 1df53ab6..19ab3e78 100644 --- a/nsd/tclcmds.c +++ b/nsd/tclcmds.c @@ -200,14 +200,17 @@ static const Cmd servCmds[] = { {"ns_cache_configure", NULL, NsTclCacheConfigureObjCmd}, {"ns_cache_create", NULL, NsTclCacheCreateObjCmd}, {"ns_cache_eval", NULL, NsTclCacheEvalObjCmd}, - {"ns_cache_flush", NULL, NsTclCacheFlushObjCmd}, {"ns_cache_exists", NULL, NsTclCacheExistsObjCmd}, + {"ns_cache_flush", NULL, NsTclCacheFlushObjCmd}, {"ns_cache_get", NULL, NsTclCacheGetObjCmd}, {"ns_cache_incr", NULL, NsTclCacheIncrObjCmd}, {"ns_cache_keys", NULL, NsTclCacheKeysObjCmd}, {"ns_cache_lappend", NULL, NsTclCacheLappendObjCmd}, {"ns_cache_names", NULL, NsTclCacheNamesObjCmd}, {"ns_cache_stats", NULL, NsTclCacheStatsObjCmd}, + {"ns_cache_transaction_begin", NULL, NsTclCacheTransactionBeginObjCmd}, + {"ns_cache_transaction_commit", NULL, NsTclCacheTransactionCommitObjCmd}, + {"ns_cache_transaction_rollback", NULL, NsTclCacheTransactionRollbackObjCmd}, {"ns_chan", NULL, NsTclChanObjCmd}, {"ns_checkurl", NULL, NsTclRequestAuthorizeObjCmd}, {"ns_cond", NULL, NsTclCondObjCmd}, diff --git a/nsd/tclfile.c b/nsd/tclfile.c index 5f5b1b8c..4b5e42b3 100644 --- a/nsd/tclfile.c +++ b/nsd/tclfile.c @@ -263,12 +263,14 @@ NsTclMkTempObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, T char buffer[PATH_MAX] = ""; snprintf(buffer, sizeof(buffer), "%s/ns-XXXXXX", nsconf.tmpDir); - Tcl_SetObjResult(interp, Tcl_NewStringObj(mktemp(buffer), -1)); - + Tcl_SetObjResult(interp, Tcl_NewStringObj(mktemp(buffer), -1)); + } else /*if (objc == 2)*/ { - char *buffer = ns_strdup(templateString); + char *buffer; - Tcl_SetResult(interp, mktemp(buffer), (Tcl_FreeProc *)ns_free); + assert(templateString != NULL); + buffer = ns_strdup(templateString); + Tcl_SetResult(interp, mktemp(buffer), (Tcl_FreeProc *)ns_free); } return result; @@ -384,7 +386,6 @@ NsTclSymlinkObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, int NsTclWriteFpObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) { - const NsInterp *itPtr = clientData; Tcl_Channel chan; int nbytes = INT_MAX, result = TCL_OK; char *fileidString; @@ -405,7 +406,8 @@ NsTclWriteFpObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj /* * All parameters are ok. */ - Ns_ReturnCode status = Ns_ConnSendChannel(itPtr->conn, chan, (size_t)nbytes); + const NsInterp *itPtr = clientData; + Ns_ReturnCode status = Ns_ConnSendChannel(itPtr->conn, chan, (size_t)nbytes); if (unlikely(status != NS_OK)) { Ns_TclPrintfResult(interp, "I/O failed"); diff --git a/nsd/tclhttp.c b/nsd/tclhttp.c index 7dfb555f..4f2482b1 100644 --- a/nsd/tclhttp.c +++ b/nsd/tclhttp.c @@ -140,8 +140,8 @@ HttpWaitObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CON { NsInterp *itPtr = clientData; Tcl_Obj *valPtr, - *elapsedVarPtr = NULL, *resultVarPtr = NULL, - *statusVarPtr = NULL, *fileVarPtr = NULL; + *elapsedVarPtr = NULL, *resultVarPtr = NULL, + *statusVarPtr = NULL, *fileVarPtr = NULL; Ns_Time *timeoutPtr = NULL; char *id = NULL; Ns_Set *hdrPtr = NULL; @@ -172,7 +172,7 @@ HttpWaitObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CON } if (HttpGet(itPtr, id, &httpPtr, NS_TRUE) == NS_FALSE) { - return TCL_ERROR; + return TCL_ERROR; } if (decompress != 0) { httpPtr->flags |= NS_HTTP_FLAG_DECOMPRESS; @@ -196,38 +196,38 @@ HttpWaitObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CON Ns_HttpCheckSpool(httpPtr); if (Ns_TaskWait(httpPtr->task, timeoutPtr) != NS_OK) { - HttpCancel(httpPtr); + HttpCancel(httpPtr); Ns_TclPrintfResult(interp, "timeout waiting for task"); - return TCL_ERROR; + return TCL_ERROR; } if (elapsedVarPtr != NULL) { - Ns_DiffTime(&httpPtr->etime, &httpPtr->stime, &diff); - valPtr = Tcl_NewObj(); - Ns_TclSetTimeObj(valPtr, &diff); - if (Ns_SetNamedVar(interp, elapsedVarPtr, valPtr) == NS_FALSE) { - goto err; - } + Ns_DiffTime(&httpPtr->etime, &httpPtr->stime, &diff); + valPtr = Tcl_NewObj(); + Ns_TclSetTimeObj(valPtr, &diff); + if (Ns_SetNamedVar(interp, elapsedVarPtr, valPtr) == NS_FALSE) { + goto err; + } } if (httpPtr->error != NULL) { Ns_TclPrintfResult(interp, "ns_http failed: %s", httpPtr->error); - goto err; + goto err; } if (httpPtr->replyHeaderSize == 0) { - Ns_HttpCheckHeader(httpPtr); + Ns_HttpCheckHeader(httpPtr); } Ns_HttpCheckSpool(httpPtr); if (statusVarPtr != NULL - && Ns_SetNamedVar(interp, statusVarPtr, Tcl_NewIntObj(httpPtr->status)) == NS_FALSE) { - goto err; + && Ns_SetNamedVar(interp, statusVarPtr, Tcl_NewIntObj(httpPtr->status)) == NS_FALSE) { + goto err; } if (httpPtr->spoolFd > 0) { - (void) ns_close(httpPtr->spoolFd); - valPtr = Tcl_NewObj(); + (void) ns_close(httpPtr->spoolFd); + valPtr = Tcl_NewObj(); } else { bool binary = NS_TRUE; @@ -261,18 +261,18 @@ HttpWaitObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CON } if (fileVarPtr != NULL - && httpPtr->spoolFd > 0 - && Ns_SetNamedVar(interp, fileVarPtr, Tcl_NewStringObj(httpPtr->spoolFileName, -1)) == NS_FALSE) { - goto err; + && httpPtr->spoolFd > 0 + && Ns_SetNamedVar(interp, fileVarPtr, Tcl_NewStringObj(httpPtr->spoolFileName, -1)) == NS_FALSE) { + goto err; } if (resultVarPtr == NULL) { - Tcl_SetObjResult(interp, valPtr); + Tcl_SetObjResult(interp, valPtr); } else { - if (Ns_SetNamedVar(interp, resultVarPtr, valPtr) == NS_FALSE) { - goto err; - } - Tcl_SetBooleanObj(Tcl_GetObjResult(interp), 1); + if (Ns_SetNamedVar(interp, resultVarPtr, valPtr) == NS_FALSE) { + goto err; + } + Tcl_SetBooleanObj(Tcl_GetObjResult(interp), 1); } result = TCL_OK; @@ -489,7 +489,7 @@ HttpQueueCmd(NsInterp *itPtr, int objc, Tcl_Obj *CONST* objv, int run) verifyInt == 1 ? NS_TRUE : NS_FALSE, keepInt == 1 ? NS_TRUE : NS_FALSE, &httpPtr) != TCL_OK) { - result = TCL_ERROR; + result = TCL_ERROR; } else { Ns_GetTime(&httpPtr->stime); @@ -521,19 +521,24 @@ HttpQueueCmd(NsInterp *itPtr, int objc, Tcl_Obj *CONST* objv, int run) if (result == TCL_OK) { Tcl_HashEntry *hPtr; - int isNew, i; + uint32_t i; + int len; char buf[TCL_INTEGER_SPACE + 4]; /* * Create a unique ID for this interp */ - i = itPtr->httpRequests.numEntries; - do { - snprintf(buf, sizeof(buf), "http%d", i++); + memcpy(buf, "http", 4u); + for( i = (uint32_t)itPtr->httpRequests.numEntries; ; i++) { + int isNew; + + len = ns_uint32toa(&buf[4], (uint32_t)i); hPtr = Tcl_CreateHashEntry(&itPtr->httpRequests, buf, &isNew); - } while (isNew == 0); + if (isNew != 0) { + break; + } + } Tcl_SetHashValue(hPtr, httpPtr); - - Tcl_SetObjResult(interp, Tcl_NewStringObj(buf, -1)); + Tcl_SetObjResult(interp, Tcl_NewStringObj(buf, len+4)); } } return result; @@ -574,23 +579,23 @@ HttpParseHeaders(char *response, Ns_Set *hdrPtr, int *statusPtr) sscanf(response, "HTTP/%2d.%2d %3d", &major, &minor, statusPtr); p = response; while ((eol = strchr(p, INTCHAR('\n'))) != NULL) { - size_t len; - - *eol++ = '\0'; - len = strlen(p); - if (len > 0u && p[len - 1u] == '\r') { - p[len - 1u] = '\0'; - } - if (firsthdr != 0) { - if (hdrPtr->name != NULL) { - ns_free((char *)hdrPtr->name); - } - hdrPtr->name = ns_strdup(p); - firsthdr = 0; - } else if (Ns_ParseHeader(hdrPtr, p, ToLower) != NS_OK) { - break; - } - p = eol; + size_t len; + + *eol++ = '\0'; + len = strlen(p); + if (len > 0u && p[len - 1u] == '\r') { + p[len - 1u] = '\0'; + } + if (firsthdr != 0) { + if (hdrPtr->name != NULL) { + ns_free((char *)hdrPtr->name); + } + hdrPtr->name = ns_strdup(p); + firsthdr = 0; + } else if (Ns_ParseHeader(hdrPtr, p, ToLower) != NS_OK) { + break; + } + p = eol; } } @@ -626,8 +631,8 @@ ProcessReplyHeaderFields(Ns_HttpTask *httpPtr) httpPtr->flags |= NS_HTTP_FLAG_GZIP_ENCODING; if ((httpPtr->flags & NS_HTTP_FLAG_GUNZIP) == NS_HTTP_FLAG_GUNZIP) { - httpPtr->compress = ns_calloc(1u, sizeof(Ns_CompressStream)); - (void) Ns_InflateInit(httpPtr->compress); + httpPtr->compress = ns_calloc(1u, sizeof(Ns_CompressStream)); + (void) Ns_InflateInit(httpPtr->compress); } } } @@ -660,26 +665,26 @@ Ns_HttpCheckHeader(Ns_HttpTask *httpPtr) NS_NONNULL_ASSERT(httpPtr != NULL); if (httpPtr->replyHeaderSize == 0) { - Ns_MutexLock(&httpPtr->lock); - if (httpPtr->replyHeaderSize == 0) { + Ns_MutexLock(&httpPtr->lock); + if (httpPtr->replyHeaderSize == 0) { char *eoh; - eoh = strstr(httpPtr->ds.string, "\r\n\r\n"); - if (eoh != NULL) { - httpPtr->replyHeaderSize = (int)(eoh - httpPtr->ds.string) + 4; - eoh += 2; - *eoh = '\0'; - } else { - eoh = strstr(httpPtr->ds.string, "\n\n"); - if (eoh != NULL) { - Ns_Log(Warning, "HttpCheckHeader: http client reply contains no crlf, this should not happen"); - httpPtr->replyHeaderSize = (int)(eoh - httpPtr->ds.string) + 2; - eoh += 1; - *eoh = '\0'; - } - } - } - Ns_MutexUnlock(&httpPtr->lock); + eoh = strstr(httpPtr->ds.string, "\r\n\r\n"); + if (eoh != NULL) { + httpPtr->replyHeaderSize = (int)(eoh - httpPtr->ds.string) + 4; + eoh += 2; + *eoh = '\0'; + } else { + eoh = strstr(httpPtr->ds.string, "\n\n"); + if (eoh != NULL) { + Ns_Log(Warning, "HttpCheckHeader: http client reply contains no crlf, this should not happen"); + httpPtr->replyHeaderSize = (int)(eoh - httpPtr->ds.string) + 2; + eoh += 1; + *eoh = '\0'; + } + } + } + Ns_MutexUnlock(&httpPtr->lock); } } @@ -714,79 +719,79 @@ Ns_HttpCheckSpool(Ns_HttpTask *httpPtr) * the reply, indicated by the available replyHeaders. */ if (httpPtr->replyHeaderSize > 0 && httpPtr->status == 0 && httpPtr->replyHeaders != NULL) { - size_t contentSize = (size_t)httpPtr->ds.length - (size_t)httpPtr->replyHeaderSize; + size_t contentSize = (size_t)httpPtr->ds.length - (size_t)httpPtr->replyHeaderSize; - Ns_MutexLock(&httpPtr->lock); - if (httpPtr->replyHeaderSize > 0 && httpPtr->status == 0) { - Tcl_WideInt length; + Ns_MutexLock(&httpPtr->lock); + if (httpPtr->replyHeaderSize > 0 && httpPtr->status == 0) { + Tcl_WideInt length; - assert(httpPtr->replyHeaders != NULL); - HttpParseHeaders(httpPtr->ds.string, httpPtr->replyHeaders, &httpPtr->status); - if (httpPtr->status == 0) { - Ns_Log(Warning, "ns_http: Parsing reply header failed"); - } - ProcessReplyHeaderFields(httpPtr); + assert(httpPtr->replyHeaders != NULL); + HttpParseHeaders(httpPtr->ds.string, httpPtr->replyHeaders, &httpPtr->status); + if (httpPtr->status == 0) { + Ns_Log(Warning, "ns_http: Parsing reply header failed"); + } + ProcessReplyHeaderFields(httpPtr); - if (httpPtr->spoolLimit > -1) { - const char *s = Ns_SetIGet(httpPtr->replyHeaders, "content-length"); + if (httpPtr->spoolLimit > -1) { + const char *s = Ns_SetIGet(httpPtr->replyHeaders, "content-length"); - if ((s != NULL - && Ns_StrToWideInt(s, &length) == NS_OK + if ((s != NULL + && Ns_StrToWideInt(s, &length) == NS_OK && length > 0 - && length >= httpPtr->spoolLimit - ) || (int)contentSize >= httpPtr->spoolLimit - ) { - int fd; + && length >= httpPtr->spoolLimit + ) || (int)contentSize >= httpPtr->spoolLimit + ) { + int fd; char *spoolFileName; - /* - * We have a valid reply length, which is larger - * than the spool limit, or the we have an actual - * content larger than the limit. Create a - * temporary spool file and rember its fd finally - * in httpPtr->spoolFd to flag that later receives - * will write there. - */ - spoolFileName = ns_malloc(strlen(nsconf.tmpDir) + 13u); - sprintf(spoolFileName, "%s/http.XXXXXX", nsconf.tmpDir); + /* + * We have a valid reply length, which is larger + * than the spool limit, or the we have an actual + * content larger than the limit. Create a + * temporary spool file and rember its fd finally + * in httpPtr->spoolFd to flag that later receives + * will write there. + */ + spoolFileName = ns_malloc(strlen(nsconf.tmpDir) + 13u); + sprintf(spoolFileName, "%s/http.XXXXXX", nsconf.tmpDir); httpPtr->spoolFileName = spoolFileName; - fd = ns_mkstemp(spoolFileName); - - if (fd == NS_INVALID_FD) { - Ns_Log(Error, "ns_http: cannot create spool file with template '%s': %s", - httpPtr->spoolFileName, strerror(errno)); - } - - if (fd != 0) { - /*Ns_Log(Ns_LogTaskDebug, "ns_http: we spool %d bytes to fd %d", contentSize, fd); */ - httpPtr->spoolFd = fd; - Ns_HttpAppendBuffer(httpPtr, - httpPtr->ds.string + httpPtr->replyHeaderSize, - contentSize); - } - } - } - } - Ns_MutexUnlock(&httpPtr->lock); - - if (contentSize > 0u && httpPtr->spoolFd == 0) { - Tcl_DString ds, *dsPtr = &ds; - - /* - * We have in httpPtr->ds the header and some content. We - * might have to decompress the first content chunk and to - * replace the compressed content with the decompressed. - */ - - Ns_Log(Ns_LogTaskDebug, "ns_http: got header %d + %" PRIdz " bytes", httpPtr->replyHeaderSize, contentSize); - - Tcl_DStringInit(dsPtr); - Tcl_DStringAppend(dsPtr, httpPtr->ds.string + httpPtr->replyHeaderSize, (int)contentSize); - Tcl_DStringTrunc(&httpPtr->ds, httpPtr->replyHeaderSize); - Ns_HttpAppendBuffer(httpPtr, dsPtr->string, contentSize); - - Tcl_DStringFree(dsPtr); - } + fd = ns_mkstemp(spoolFileName); + + if (fd == NS_INVALID_FD) { + Ns_Log(Error, "ns_http: cannot create spool file with template '%s': %s", + httpPtr->spoolFileName, strerror(errno)); + } + + if (fd != 0) { + /*Ns_Log(Ns_LogTaskDebug, "ns_http: we spool %d bytes to fd %d", contentSize, fd); */ + httpPtr->spoolFd = fd; + Ns_HttpAppendBuffer(httpPtr, + httpPtr->ds.string + httpPtr->replyHeaderSize, + contentSize); + } + } + } + } + Ns_MutexUnlock(&httpPtr->lock); + + if (contentSize > 0u && httpPtr->spoolFd == 0) { + Tcl_DString ds, *dsPtr = &ds; + + /* + * We have in httpPtr->ds the header and some content. We + * might have to decompress the first content chunk and to + * replace the compressed content with the decompressed. + */ + + Ns_Log(Ns_LogTaskDebug, "ns_http: got header %d + %" PRIdz " bytes", httpPtr->replyHeaderSize, contentSize); + + Tcl_DStringInit(dsPtr); + Tcl_DStringAppend(dsPtr, httpPtr->ds.string + httpPtr->replyHeaderSize, (int)contentSize); + Tcl_DStringSetLength(&httpPtr->ds, httpPtr->replyHeaderSize); + Ns_HttpAppendBuffer(httpPtr, dsPtr->string, contentSize); + + Tcl_DStringFree(dsPtr); + } } } @@ -863,13 +868,22 @@ Ns_HttpLocationString(Tcl_DString *dsPtr, const char *protoString, const char *h if (protoString != NULL) { Ns_DStringVarAppend(dsPtr, protoString, "://", (char *)0); } - if (strchr(hostString, INTCHAR(':')) != NULL) { - Ns_DStringVarAppend(dsPtr, "[", hostString, "]", (char *)0); - } else { + if (port == 0 && defPort == 0) { + /* + * We assume, that the host contains already a port (as provided from + * the host header field), and all we have to do is to prepend the + * protocol prefix. + */ Ns_DStringVarAppend(dsPtr, hostString, (char *)0); - } - if (port != defPort) { - (void) Ns_DStringPrintf(dsPtr, ":%d", port); + } else { + if (strchr(hostString, INTCHAR(':')) != NULL) { + Ns_DStringVarAppend(dsPtr, "[", hostString, "]", (char *)0); + } else { + Ns_DStringVarAppend(dsPtr, hostString, (char *)0); + } + if (port != defPort) { + (void) Ns_DStringPrintf(dsPtr, ":%d", port); + } } return dsPtr->string; } @@ -1027,7 +1041,7 @@ HttpConnect(Tcl_Interp *interp, const char *method, const char *url, if (keep_host_header) { if ( (hdrPtr == NULL) || (Ns_SetIFind(hdrPtr, "Host") == -1) ) { Ns_TclPrintfResult(interp, "keep_host_header specified but no Host header given"); - return TCL_ERROR; + return TCL_ERROR; } } @@ -1079,7 +1093,7 @@ HttpConnect(Tcl_Interp *interp, const char *method, const char *url, sock = Ns_SockAsyncConnect(host, portNr); if (sock == NS_INVALID_SOCKET) { - Ns_TclPrintfResult(interp, "connect to \"%s\" failed: %s", + Ns_TclPrintfResult(interp, "connect to \"%s\" failed: %s", url, ns_sockstrerror(ns_sockerrno)); goto fail; } @@ -1176,24 +1190,24 @@ HttpConnect(Tcl_Interp *interp, const char *method, const char *url, * Submit provided headers */ if (hdrPtr != NULL) { - size_t i; + size_t i; - /* - * Remove the header fields, we are providing - */ + /* + * Remove the header fields, we are providing + */ if (!keep_host_header) { - Ns_SetIDeleteKey(hdrPtr, "Host"); + Ns_SetIDeleteKey(hdrPtr, "Host"); + } + Ns_SetIDeleteKey(hdrPtr, "Connection"); + Ns_SetIDeleteKey(hdrPtr, "Content-Length"); + + for (i = 0u; i < Ns_SetSize(hdrPtr); i++) { + const char *key = Ns_SetKey(hdrPtr, i); + if (uaFlag != 0) { + uaFlag = strcasecmp(key, "User-Agent"); + } + Ns_DStringPrintf(dsPtr, "%s: %s\r\n", key, Ns_SetValue(hdrPtr, i)); } - Ns_SetIDeleteKey(hdrPtr, "Connection"); - Ns_SetIDeleteKey(hdrPtr, "Content-Length"); - - for (i = 0u; i < Ns_SetSize(hdrPtr); i++) { - const char *key = Ns_SetKey(hdrPtr, i); - if (uaFlag != 0) { - uaFlag = strcasecmp(key, "User-Agent"); - } - Ns_DStringPrintf(dsPtr, "%s: %s\r\n", key, Ns_SetValue(hdrPtr, i)); - } } /* @@ -1205,9 +1219,9 @@ HttpConnect(Tcl_Interp *interp, const char *method, const char *url, * User-Agent header was not supplied, add our own */ if (uaFlag != 0) { - Ns_DStringPrintf(dsPtr, "User-Agent: %s/%s\r\n", - Ns_InfoServerName(), - Ns_InfoServerVersion()); + Ns_DStringPrintf(dsPtr, "User-Agent: %s/%s\r\n", + Ns_InfoServerName(), + Ns_InfoServerVersion()); } if (!keep_host_header) { @@ -1294,14 +1308,14 @@ HttpAppendRawBuffer(Ns_HttpTask *httpPtr, const char *buffer, size_t outSize) NS_NONNULL_ASSERT(buffer != NULL); if (httpPtr->spoolFd > 0) { - ssize_t written = ns_write(httpPtr->spoolFd, buffer, outSize); + ssize_t written = ns_write(httpPtr->spoolFd, buffer, outSize); - if (written == -1) { - Ns_Log(Error, "task: spooling of received content failed"); - status = TCL_ERROR; - } + if (written == -1) { + Ns_Log(Error, "task: spooling of received content failed"); + status = TCL_ERROR; + } } else { - Tcl_DStringAppend(&httpPtr->ds, buffer, (int)outSize); + Tcl_DStringAppend(&httpPtr->ds, buffer, (int)outSize); } return status; @@ -1318,31 +1332,31 @@ Ns_HttpAppendBuffer(Ns_HttpTask *httpPtr, const char *buffer, size_t inSize) Ns_Log(Ns_LogTaskDebug, "Ns_HttpAppendBuffer: got %" PRIdz " bytes flags %.6x", inSize, httpPtr->flags); if (likely((httpPtr->flags & NS_HTTP_FLAG_GUNZIP) != NS_HTTP_FLAG_GUNZIP)) { - /* - * Output raw content - */ - result = HttpAppendRawBuffer(httpPtr, buffer, inSize); + /* + * Output raw content + */ + result = HttpAppendRawBuffer(httpPtr, buffer, inSize); } else { - char out[16384]; + char out[16384]; out[0] = '\0'; - /* - * Output decompressed content - */ - (void) Ns_InflateBufferInit(httpPtr->compress, buffer, inSize); - Ns_Log(Ns_LogTaskDebug, "InflateBuffer: got %" PRIdz " compressed bytes", inSize); - do { - size_t uncompressedLen = 0u; - - result = Ns_InflateBuffer(httpPtr->compress, out, sizeof(out), &uncompressedLen); - Ns_Log(Ns_LogTaskDebug, "InflateBuffer status %d uncompressed %" PRIdz " bytes", result, uncompressedLen); - - if (HttpAppendRawBuffer(httpPtr, out, uncompressedLen) != TCL_OK) { + /* + * Output decompressed content + */ + (void) Ns_InflateBufferInit(httpPtr->compress, buffer, inSize); + Ns_Log(Ns_LogTaskDebug, "InflateBuffer: got %" PRIdz " compressed bytes", inSize); + do { + size_t uncompressedLen = 0u; + + result = Ns_InflateBuffer(httpPtr->compress, out, sizeof(out), &uncompressedLen); + Ns_Log(Ns_LogTaskDebug, "InflateBuffer status %d uncompressed %" PRIdz " bytes", result, uncompressedLen); + + if (HttpAppendRawBuffer(httpPtr, out, uncompressedLen) != TCL_OK) { result = TCL_ERROR; } - } while(result == TCL_CONTINUE); + } while(result == TCL_CONTINUE); } return result; } @@ -1381,8 +1395,8 @@ HttpClose(Ns_HttpTask *httpPtr) if (httpPtr->spoolFd > 0) {(void) ns_close(httpPtr->spoolFd);} if (httpPtr->bodyFileFd > 0) {(void) ns_close(httpPtr->bodyFileFd);} if (httpPtr->compress != NULL) { - (void) Ns_InflateEnd(httpPtr->compress); - ns_free(httpPtr->compress); + (void) Ns_InflateEnd(httpPtr->compress); + ns_free(httpPtr->compress); } Ns_MutexDestroy(&httpPtr->lock); Tcl_DStringFree(&httpPtr->ds); @@ -1511,28 +1525,28 @@ HttpTaskRecv(const Ns_HttpTask *httpPtr, char *buffer, size_t length) #ifdef HAVE_OPENSSL_EVP_H /*fprintf(stderr, "### SSL_read want %lu\n", length);*/ - received = 0; + received = 0; for (;;) { int n, err; - n = SSL_read(httpPtr->ssl, buffer+received, (int)(length - (size_t)received)); - err = SSL_get_error(httpPtr->ssl, n); - /*fprintf(stderr, "### SSL_read n %ld got %lu err %d\n", n, received, err);*/ - switch (err) { - case SSL_ERROR_NONE: - if (n < 0) { - Ns_Log(Error, "SSL_read failed but no error, should not happen"); - break; - } - received += n; - break; - - case SSL_ERROR_WANT_READ: - /*fprintf(stderr, "### partial read, n %d\n", (int)n); */ - received += n; - continue; - } - break; + n = SSL_read(httpPtr->ssl, buffer+received, (int)(length - (size_t)received)); + err = SSL_get_error(httpPtr->ssl, n); + /*fprintf(stderr, "### SSL_read n %ld got %lu err %d\n", n, received, err);*/ + switch (err) { + case SSL_ERROR_NONE: + if (n < 0) { + Ns_Log(Error, "SSL_read failed but no error, should not happen"); + break; + } + received += n; + break; + + case SSL_ERROR_WANT_READ: + /*fprintf(stderr, "### partial read, n %d\n", (int)n); */ + received += n; + continue; + } + break; } #else received = -1; @@ -1606,7 +1620,7 @@ HttpProc(Ns_Task *task, NS_SOCKET UNUSED(sock), void *arg, Ns_SockState why) switch (why) { case NS_SOCK_INIT: - Ns_TaskCallback(task, NS_SOCK_WRITE, &httpPtr->timeout); + Ns_TaskCallback(task, NS_SOCK_WRITE, &httpPtr->timeout); taskDone = NS_FALSE; break; @@ -1629,7 +1643,7 @@ HttpProc(Ns_Task *task, NS_SOCKET UNUSED(sock), void *arg, Ns_SockState why) if (n < CHUNK_SIZE) { Ns_Log(Ns_LogTaskDebug, "HttpProc all data spooled, switch to read reply"); HttpTaskShutdown(httpPtr); - Tcl_DStringTrunc(&httpPtr->ds, 0); + Tcl_DStringSetLength(&httpPtr->ds, 0); Ns_TaskCallback(task, NS_SOCK_READ, &httpPtr->timeout); } taskDone = NS_FALSE; @@ -1652,73 +1666,73 @@ HttpProc(Ns_Task *task, NS_SOCKET UNUSED(sock), void *arg, Ns_SockState why) if (httpPtr->bodyFileFd > 0) { httpPtr->sendSpoolMode = NS_TRUE; Ns_Log(Ns_LogTaskDebug, "HttpProc all data sent, switch to spool mode using fd %d", httpPtr->bodyFileFd); - Tcl_DStringTrunc(&httpPtr->ds, CHUNK_SIZE); + Tcl_DStringSetLength(&httpPtr->ds, CHUNK_SIZE); } else { Ns_Log(Ns_LogTaskDebug, "HttpProc all data sent, switch to read reply"); HttpTaskShutdown(httpPtr); - Tcl_DStringTrunc(&httpPtr->ds, 0); + Tcl_DStringSetLength(&httpPtr->ds, 0); Ns_TaskCallback(task, NS_SOCK_READ, &httpPtr->timeout); } } taskDone = NS_FALSE; } } - break; + break; case NS_SOCK_READ: - n = HttpTaskRecv(httpPtr, buf, sizeof(buf)); - - if (likely(n > 0)) { - - /* - * Spooling is only activated after (a) having processed - * the headers, and (b) after the wait command has - * required to spool. Once we know spoolFd, there is no - * need to HttpCheckHeader() again. - */ - if (httpPtr->spoolFd > 0) { - (void) Ns_HttpAppendBuffer(httpPtr, buf, (size_t)n); - } else { - Ns_Log(Ns_LogTaskDebug, "Task got %d bytes", (int)n); - - (void) Ns_HttpAppendBuffer(httpPtr, buf, (size_t)n); - - if (unlikely(httpPtr->replyHeaderSize == 0)) { - Ns_HttpCheckHeader(httpPtr); - } - /* - * Ns_HttpCheckSpool might set httpPtr->spoolFd - */ - Ns_HttpCheckSpool(httpPtr); - /*Ns_Log(Ns_LogTaskDebug, "Task got %d bytes, header = %d", (int)n, httpPtr->replyHeaderSize);*/ - } + n = HttpTaskRecv(httpPtr, buf, sizeof(buf)); + + if (likely(n > 0)) { + + /* + * Spooling is only activated after (a) having processed + * the headers, and (b) after the wait command has + * required to spool. Once we know spoolFd, there is no + * need to HttpCheckHeader() again. + */ + if (httpPtr->spoolFd > 0) { + (void) Ns_HttpAppendBuffer(httpPtr, buf, (size_t)n); + } else { + Ns_Log(Ns_LogTaskDebug, "Task got %d bytes", (int)n); + + (void) Ns_HttpAppendBuffer(httpPtr, buf, (size_t)n); + + if (unlikely(httpPtr->replyHeaderSize == 0)) { + Ns_HttpCheckHeader(httpPtr); + } + /* + * Ns_HttpCheckSpool might set httpPtr->spoolFd + */ + Ns_HttpCheckSpool(httpPtr); + /*Ns_Log(Ns_LogTaskDebug, "Task got %d bytes, header = %d", (int)n, httpPtr->replyHeaderSize);*/ + } taskDone = NS_FALSE; - } - if (n < 0) { + } + if (n < 0) { Ns_Log(Warning, "client http request: receive failed, error: %s\n", strerror(errno)); - httpPtr->error = "recv failed"; - } - break; + httpPtr->error = "recv failed"; + } + break; case NS_SOCK_DONE: taskDone = NS_FALSE; break; case NS_SOCK_TIMEOUT: - httpPtr->error = "timeout"; - break; + httpPtr->error = "timeout"; + break; case NS_SOCK_EXIT: - httpPtr->error = "shutdown"; - break; + httpPtr->error = "shutdown"; + break; case NS_SOCK_CANCEL: - httpPtr->error = "cancelled"; - break; + httpPtr->error = "cancelled"; + break; case NS_SOCK_EXCEPTION: - httpPtr->error = "exception"; - break; + httpPtr->error = "exception"; + break; } if (taskDone) { diff --git a/nsd/tclinit.c b/nsd/tclinit.c index a4fc488e..bd37f5d1 100644 --- a/nsd/tclinit.c +++ b/nsd/tclinit.c @@ -231,75 +231,82 @@ static Ns_ReturnCode ConfigServerTcl(const char *server) { NsServer *servPtr; - Ns_DString ds; - const char *path, *p, *initFileString; - int n; - Ns_Set *set; + Ns_ReturnCode result; NS_NONNULL_ASSERT(server != NULL); servPtr = NsGetServer(server); - assert(servPtr != NULL); - path = Ns_ConfigGetPath(server, NULL, "tcl", (char *)0); - set = Ns_ConfigCreateSection(path); + if (unlikely(servPtr == NULL)) { + Ns_Log(Warning, "Could configure Tcl; server '%s' unknown", server); + result = NS_ERROR; - Ns_DStringInit(&ds); + } else { + Ns_DString ds; + const char *path, *p, *initFileString; + int n; + Ns_Set *set; - servPtr->tcl.library = Ns_ConfigString(path, "library", "modules/tcl"); - if (Ns_PathIsAbsolute(servPtr->tcl.library) == NS_FALSE) { - Ns_HomePath(&ds, servPtr->tcl.library, (char *)0); - servPtr->tcl.library = Ns_DStringExport(&ds); - Ns_SetUpdate(set, "library", servPtr->tcl.library); - } + path = Ns_ConfigGetPath(server, NULL, "tcl", (char *)0); + set = Ns_ConfigCreateSection(path); - initFileString = Ns_ConfigString(path, "initfile", "bin/init.tcl"); - if (Ns_PathIsAbsolute(initFileString) == NS_FALSE) { - Ns_HomePath(&ds, initFileString, (char *)0); - initFileString = Ns_DStringExport(&ds); - Ns_SetUpdate(set, "initfile", initFileString); - } - servPtr->tcl.initfile = Tcl_NewStringObj(initFileString, -1); - Tcl_IncrRefCount(servPtr->tcl.initfile); - - servPtr->tcl.modules = Tcl_NewObj(); - Tcl_IncrRefCount(servPtr->tcl.modules); - - Ns_RWLockInit(&servPtr->tcl.lock); - Ns_MutexInit(&servPtr->tcl.cachelock); - Ns_MutexSetName2(&servPtr->tcl.cachelock, "ns:tcl.cache", server); - Tcl_InitHashTable(&servPtr->tcl.caches, TCL_STRING_KEYS); - Tcl_InitHashTable(&servPtr->tcl.runTable, TCL_STRING_KEYS); - Tcl_InitHashTable(&servPtr->tcl.synch.mutexTable, TCL_STRING_KEYS); - Tcl_InitHashTable(&servPtr->tcl.synch.csTable, TCL_STRING_KEYS); - Tcl_InitHashTable(&servPtr->tcl.synch.semaTable, TCL_STRING_KEYS); - Tcl_InitHashTable(&servPtr->tcl.synch.condTable, TCL_STRING_KEYS); - Tcl_InitHashTable(&servPtr->tcl.synch.rwTable, TCL_STRING_KEYS); - - servPtr->nsv.nbuckets = Ns_ConfigIntRange(path, "nsvbuckets", 8, 1, INT_MAX); - servPtr->nsv.buckets = NsTclCreateBuckets(server, servPtr->nsv.nbuckets); + Ns_DStringInit(&ds); - /* - * Initialize the list of connection headers to log for Tcl errors. - */ + servPtr->tcl.library = Ns_ConfigString(path, "library", "modules/tcl"); + if (Ns_PathIsAbsolute(servPtr->tcl.library) == NS_FALSE) { + Ns_HomePath(&ds, servPtr->tcl.library, (char *)0); + servPtr->tcl.library = Ns_DStringExport(&ds); + Ns_SetUpdate(set, "library", servPtr->tcl.library); + } - p = Ns_ConfigGetValue(path, "errorlogheaders"); - if (p != NULL - && Tcl_SplitList(NULL, p, &n, &servPtr->tcl.errorLogHeaders) != TCL_OK) { - Ns_Log(Error, "config: errorlogheaders is not a list: %s", p); - } + initFileString = Ns_ConfigString(path, "initfile", "bin/init.tcl"); + if (Ns_PathIsAbsolute(initFileString) == NS_FALSE) { + Ns_HomePath(&ds, initFileString, (char *)0); + initFileString = Ns_DStringExport(&ds); + Ns_SetUpdate(set, "initfile", initFileString); + } + servPtr->tcl.initfile = Tcl_NewStringObj(initFileString, -1); + Tcl_IncrRefCount(servPtr->tcl.initfile); + + servPtr->tcl.modules = Tcl_NewObj(); + Tcl_IncrRefCount(servPtr->tcl.modules); + + Ns_RWLockInit(&servPtr->tcl.lock); + Ns_MutexInit(&servPtr->tcl.cachelock); + Ns_MutexSetName2(&servPtr->tcl.cachelock, "ns:tcl.cache", server); + Tcl_InitHashTable(&servPtr->tcl.caches, TCL_STRING_KEYS); + Tcl_InitHashTable(&servPtr->tcl.runTable, TCL_STRING_KEYS); + Tcl_InitHashTable(&servPtr->tcl.synch.mutexTable, TCL_STRING_KEYS); + Tcl_InitHashTable(&servPtr->tcl.synch.csTable, TCL_STRING_KEYS); + Tcl_InitHashTable(&servPtr->tcl.synch.semaTable, TCL_STRING_KEYS); + Tcl_InitHashTable(&servPtr->tcl.synch.condTable, TCL_STRING_KEYS); + Tcl_InitHashTable(&servPtr->tcl.synch.rwTable, TCL_STRING_KEYS); + + servPtr->nsv.nbuckets = Ns_ConfigIntRange(path, "nsvbuckets", 8, 1, INT_MAX); + servPtr->nsv.buckets = NsTclCreateBuckets(server, servPtr->nsv.nbuckets); - /* - * Initialize the Tcl detached channel support. - */ + /* + * Initialize the list of connection headers to log for Tcl errors. + */ - Tcl_InitHashTable(&servPtr->chans.table, TCL_STRING_KEYS); - Ns_MutexSetName2(&servPtr->chans.lock, "nstcl:chans", server); + p = Ns_ConfigGetValue(path, "errorlogheaders"); + if (p != NULL + && Tcl_SplitList(NULL, p, &n, &servPtr->tcl.errorLogHeaders) != TCL_OK) { + Ns_Log(Error, "config: errorlogheaders is not a list: %s", p); + } - Tcl_InitHashTable(&servPtr->connchans.table, TCL_STRING_KEYS); - Ns_MutexSetName2(&servPtr->connchans.lock, "nstcl:connchans", server); + /* + * Initialize the Tcl detached channel support. + */ - return NS_OK; + Tcl_InitHashTable(&servPtr->chans.table, TCL_STRING_KEYS); + Ns_MutexSetName2(&servPtr->chans.lock, "nstcl:chans", server); + + Tcl_InitHashTable(&servPtr->connchans.table, TCL_STRING_KEYS); + Ns_MutexSetName2(&servPtr->connchans.lock, "nstcl:connchans", server); + result = NS_OK; + } + return result; } @@ -735,7 +742,7 @@ Ns_TclRegisterTrace(const char *server, Ns_TclTraceProc *proc, /* * Run CREATE and ALLOCATE traces immediately so that commands registered * by binary modules can be called by Tcl init scripts sourced by the - * already initialised interp which loads the modules. + * already initialized interp which loads the modules. */ if ((when == NS_TCL_TRACE_CREATE) || (when == NS_TCL_TRACE_ALLOCATE)) { @@ -1344,7 +1351,7 @@ ICtlCleanupObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj * (*deferPtr->proc)(interp, deferPtr->arg); ns_free(deferPtr); } - itPtr->firstDeferPtr = NULL; + itPtr->firstDeferPtr = NULL; result = UpdateInterp(itPtr); } @@ -1653,7 +1660,7 @@ NsTclInitServer(const char *server) servPtr = NsGetServer(server); if (servPtr != NULL) { - Tcl_Interp *interp = NsTclAllocateInterp(servPtr); + Tcl_Interp *interp = NsTclAllocateInterp(servPtr); if ( Tcl_FSEvalFile(interp, servPtr->tcl.initfile) != TCL_OK) { (void) Ns_TclLogErrorInfo(interp, "\n(context: init server)"); @@ -2020,11 +2027,11 @@ CreateInterp(NsInterp **itPtrPtr, NsServer *servPtr) * operation only once for all threads. */ if (strcmp("utf-8", Tcl_GetEncodingName(Tcl_GetEncoding(interp, NULL))) != 0) { - int result = Tcl_SetSystemEncoding(interp, "utf-8"); - - if (result != TCL_OK) { - (void) Ns_TclLogErrorInfo(interp, "\n(context: set system encoding to utf-8)"); - } + int result = Tcl_SetSystemEncoding(interp, "utf-8"); + + if (result != TCL_OK) { + (void) Ns_TclLogErrorInfo(interp, "\n(context: set system encoding to utf-8)"); + } } /* @@ -2059,7 +2066,7 @@ CreateInterp(NsInterp **itPtrPtr, NsServer *servPtr) static NsInterp * NewInterpData(Tcl_Interp *interp, NsServer *servPtr) { - static volatile int initialized = 0; + static volatile bool initialized = NS_FALSE; NsInterp *itPtr; NS_NONNULL_ASSERT(interp != NULL); @@ -2070,14 +2077,14 @@ NewInterpData(Tcl_Interp *interp, NsServer *servPtr) * Tcl is not fully initialized at libnsd load time. */ - if (initialized == 0) { + if (!initialized) { Ns_MasterLock(); - if (initialized == 0) { + if (!initialized) { NsTclInitQueueType(); NsTclInitAddrType(); NsTclInitTimeType(); NsTclInitKeylistType(); - initialized = 1; + initialized = NS_TRUE; } Ns_MasterUnlock(); } @@ -2329,7 +2336,7 @@ DeleteInterps(void *arg) itPtr = Tcl_GetHashValue(hPtr); if ((itPtr != NULL) && (itPtr->interp != NULL)) { - Ns_TclDestroyInterp(itPtr->interp); + Ns_TclDestroyInterp(itPtr->interp); } hPtr = Tcl_NextHashEntry(&search); } diff --git a/nsd/tcljob.c b/nsd/tcljob.c index 153fc455..1c0ea723 100644 --- a/nsd/tcljob.c +++ b/nsd/tcljob.c @@ -35,8 +35,8 @@ * * Lock rules: * - * o. lock the queuelock when modifing tp structure elements. - * o. lock the queue's lock when modifing queue structure elements. + * o. lock the queuelock when modifying tp structure elements. + * o. lock the queue's lock when modifying queue structure elements. * o. jobs are shared between tp and the queue but are owned by the * queue, so use queue's lock is used to control access to the * jobs. @@ -72,7 +72,7 @@ * cleans it up. It order to help the user out we would like to add * an "-autoclean" option to queue create function. However, * AOLServer does not currently supply a "good" connection cleanup - * callback. We tryed to use "Ns_RegisterConnCleanup" however it + * callback. We tried to use "Ns_RegisterConnCleanup" however it * does not have a facility to remove registered callbacks. * */ @@ -161,7 +161,7 @@ typedef struct Queue { /* - * A threadpool mananges a global set of threads. + * A threadpool manages a global set of threads. */ typedef struct ThreadPool { Ns_Cond cond; @@ -259,12 +259,9 @@ static double ComputeDelta(const Ns_Time *start, const Ns_Time *end) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2); - - /* * Globals */ - static ThreadPool tp; @@ -273,13 +270,13 @@ static ThreadPool tp; * * NsInitTclQueueType -- * - * Initialize the Tcl job queue. + * Initialize the Tcl job queue. * * Results: - * None. + * None. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ @@ -307,17 +304,17 @@ NsTclInitQueueType(void) * * NsStartJobsShutdown -- * - * Signal stop of the Tcl job threads. + * Signal stop of the Tcl job threads. * * Results: - * None. + * None. * * Side effects: - * All pending jobs are removed and waiting threads interrupted. + * All pending jobs are removed and waiting threads + * interrupted. * *---------------------------------------------------------------------- */ - void NsStartJobsShutdown(void) { @@ -327,9 +324,9 @@ NsStartJobsShutdown(void) hPtr = Tcl_FirstHashEntry(&tp.queues, &search); while (hPtr != NULL) { Ns_MutexLock(&tp.queuelock); - tp.req = THREADPOOL_REQ_STOP; - Ns_CondBroadcast(&tp.cond); - Ns_MutexUnlock(&tp.queuelock); + tp.req = THREADPOOL_REQ_STOP; + Ns_CondBroadcast(&tp.cond); + Ns_MutexUnlock(&tp.queuelock); hPtr = Tcl_NextHashEntry(&search); } } @@ -340,17 +337,16 @@ NsStartJobsShutdown(void) * * NsWaitJobsShutdown -- * - * Wait for Tcl job threads to exit. + * Wait for Tcl job threads to exit. * * Results: - * None. + * None. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ - void NsWaitJobsShutdown(const Ns_Time *toPtr) { @@ -361,10 +357,10 @@ NsWaitJobsShutdown(const Ns_Time *toPtr) hPtr = Tcl_FirstHashEntry(&tp.queues, &search); while (status == NS_OK && hPtr != NULL) { Ns_MutexLock(&tp.queuelock); - while (status == NS_OK && tp.nthreads > 0) { + while (status == NS_OK && tp.nthreads > 0) { status = Ns_CondTimedWait(&tp.cond, &tp.queuelock, toPtr); - } - Ns_MutexUnlock(&tp.queuelock); + } + Ns_MutexUnlock(&tp.queuelock); hPtr = Tcl_NextHashEntry(&search); } if (status != NS_OK) { @@ -378,14 +374,14 @@ NsWaitJobsShutdown(const Ns_Time *toPtr) * * JobConfigureObjCmd, subcommand of NsTclJobCmd -- * - * Implements the "ns_job configure" command. + * Implements the "ns_job configure" command. * Configure jobs subsystem. * * Results: - * Standard Tcl result. + * Standard Tcl result. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ @@ -427,21 +423,20 @@ JobConfigureObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, * * JobCreateObjCmd, subcommand of NsTclJobCmd -- * - * Implements the "ns_job create" command. + * Implements the "ns_job create" command. * Create a new thread pool queue. * * Results: - * Standard Tcl result. + * Standard Tcl result. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ static int JobCreateObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) { - int result = TCL_OK, max = NS_JOB_DEFAULT_MAXTHREADS; Tcl_Obj *queueIdObj; char *descString = (char *)""; @@ -487,15 +482,15 @@ JobCreateObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl * * JobDeleteObjCmd, subcommand of NsTclJobCmd -- * - * Implements the "ns_job delete" command. Request that the + * Implements the "ns_job delete" command. Request that the * specified queue be deleted. The queue will only be deleted * when all jobs are removed. * * Results: - * Standard Tcl result. + * Standard Tcl result. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ @@ -529,14 +524,14 @@ JobDeleteObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl * * JobQueueObjCmd, subcommand of NsTclJobCmd -- * - * Implements the "ns_job queue" command. + * Implements the "ns_job queue" command. * Add a new job the specified queue. * * Results: - * Standard Tcl result. + * Standard Tcl result. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ @@ -548,7 +543,7 @@ JobQueueObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CON char *script = NULL, *jobIdString = NULL, *queueIdString = NULL; char buf[100]; Ns_ObjvSpec lopts[] = { - {"-detached", Ns_ObjvBool, &detached, INT2PTR(NS_TRUE)}, + {"-detached", Ns_ObjvBool, &detached, INT2PTR(NS_TRUE)}, {"-head", Ns_ObjvBool, &head, INT2PTR(NS_TRUE)}, {"-jobid", Ns_ObjvString, &jobIdString, NULL}, {NULL, NULL, NULL, NULL} @@ -602,7 +597,6 @@ JobQueueObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CON * Job id is given, try to see if it is taken already, * if yes, return error, it should be unique. */ - if (jobIdString != NULL && *jobIdString != '\0') { hPtr = Tcl_CreateHashEntry(&queue->jobs, jobIdString, &isNew); if (isNew == 0) { @@ -615,8 +609,9 @@ JobQueueObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CON /* * Add the job to queue. */ + memcpy(buf, "job", 3); do { - snprintf(buf, sizeof(buf), "job%" PRIuPTR, queue->nextid++); + (void) ns_uint64toa(&buf[3], (uint64_t)queue->nextid++); hPtr = Tcl_CreateHashEntry(&queue->jobs, buf, &isNew); } while (isNew == 0); @@ -624,11 +619,10 @@ JobQueueObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CON } /* - * Add the job to the thread pool's job list, if -head is + * Add the job to the thread pool's job list, if "-head" is * specified, insert new job at the beginning, otherwise * append new job to the end. */ - if (head != 0) { jobPtr->nextPtr = tp.firstPtr; tp.firstPtr = jobPtr; @@ -645,7 +639,6 @@ JobQueueObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CON * Start a new thread if there are less than maxThreads * currently running and there currently no idle threads. */ - if (tp.nidle == 0 && tp.nthreads < tp.maxThreads) { create = NS_TRUE; ++tp.nthreads; @@ -678,14 +671,14 @@ JobQueueObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CON * * JobWaitObjCmd, subcommand of NsTclJobCmd -- * - * Implements the "ns_job wait" command. + * Implements the "ns_job wait" command. * Wait for the specified job. * * Results: - * Standard Tcl result. + * Standard Tcl result. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ @@ -775,8 +768,8 @@ JobWaitObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_O if (hPtr == NULL || jobPtr == Tcl_GetHashValue(hPtr)) { Ns_TclPrintfResult(interp, "Internal ns_job error."); /* - * logically, there should be a "result = TCL_ERROR;" - * here. however, this would change the results of the + * Logically, there should be a "result = TCL_ERROR;" + * here. However, this would change the results of the * regression test. */ } @@ -811,14 +804,14 @@ JobWaitObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_O * * JobCancelObjCmd, subcommand of NsTclJobCmd -- * - * Implements the "ns_job cancel" command. + * Implements the "ns_job cancel" command. * Cancel the specified job. * * Results: - * Standard Tcl result. + * Standard Tcl result. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ @@ -880,14 +873,14 @@ JobCancelObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl * * JobExistsObjCmd, subcommand of NsTclJobCmd -- * - * Implements the "ns_job exists" command. Sets the - * Tcl-result to 1if job is running otherwise to 0. + * Implements the "ns_job exists" command. Sets the + * Tcl-result to "1" if job is running otherwise to "0". * * Results: - * Standard Tcl result. + * Standard Tcl result. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ @@ -907,8 +900,10 @@ JobExistsObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl result = TCL_ERROR; } else { - const Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&queue->jobs, jobIdString); + const Tcl_HashEntry *hPtr; + assert(queue != NULL); + hPtr = Tcl_FindHashEntry(&queue->jobs, jobIdString); (void)ReleaseQueue(queue, NS_FALSE); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(hPtr != NULL)); } @@ -921,14 +916,14 @@ JobExistsObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl * * JobWaitAnyObjCmd, subcommand of NsTclJobCmd -- * - * Implements the "ns_job waitany" command. + * Implements the "ns_job waitany" command. * Wait for any job on the queue complete. * * Results: - * Standard Tcl result. + * Standard Tcl result. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ @@ -996,14 +991,14 @@ JobWaitAnyObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tc * * JobJobsObjCmd, subcommand of NsTclJobCmd -- * - * Implements the "ns_job jos" command. + * Implements the "ns_job jos" command. * Returns a list of job IDs in arbitrary order. * * Results: - * Standard Tcl result. + * Standard Tcl result. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ @@ -1016,7 +1011,7 @@ JobJobsObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_O {"queueId", ObjvQueue, &queue, NULL}, {NULL, NULL, NULL, NULL} }; - + if (Ns_ParseObjv(NULL, args, interp, 2, objc, objv) != NS_OK) { result = TCL_ERROR; @@ -1025,6 +1020,7 @@ JobJobsObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_O Tcl_HashSearch search; Tcl_Obj *listObj = Tcl_NewListObj(0, NULL); + assert(queue != NULL); /* * Collect the jobIdString in the listObj. */ @@ -1047,14 +1043,14 @@ JobJobsObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_O * * JobQueuesObjCmd, subcommand of NsTclJobCmd -- * - * Implements the "ns_job queues" command. + * Implements the "ns_job queues" command. * Returns a list of the current queues. * * Results: - * Standard Tcl result. + * Standard Tcl result. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ @@ -1095,7 +1091,7 @@ JobQueuesObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl * * JobJobListObjCmd, subcommand of NsTclJobCmd -- * - * Implements the "ns_job joblist" command. + * Implements the "ns_job joblist" command. * Returns a list of all the jobs in the queue. * * Every entry of a "job" consists of: @@ -1105,10 +1101,10 @@ JobQueuesObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl * Code (Standard Tcl result code) * * Results: - * Standard Tcl result. + * Standard Tcl result. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ @@ -1121,7 +1117,7 @@ JobJobListObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tc {"queueId", ObjvQueue, &queue, NULL}, {NULL, NULL, NULL, NULL} }; - + if (Ns_ParseObjv(NULL, args, interp, 2, objc, objv) != NS_OK) { result = TCL_ERROR; @@ -1202,14 +1198,14 @@ JobJobListObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tc * * JobQueueListObjCmd, subcommand of NsTclJobCmd -- * - * Implements the "ns_job queuelist" command. Returns a list + * Implements the "ns_job queuelist" command. Returns a list * of all the queues and the queue information. * * Results: - * Standard Tcl result. + * Standard Tcl result. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ @@ -1225,7 +1221,7 @@ JobQueueListObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, const Tcl_HashEntry *hPtr; Tcl_HashSearch search; Tcl_Obj *queueList; - + /* * Create a Tcl List to hold the list of jobs. */ @@ -1280,14 +1276,14 @@ JobQueueListObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, * * JobGenIDObjCmd, subcommand of NsTclJobCmd -- * - * Implements the "ns_job genID" command. + * Implements the "ns_job genID" command. * Generate a unique queue name. * * Results: - * Standard Tcl result. + * Standard Tcl result. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ @@ -1302,13 +1298,13 @@ JobGenIDObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_ } else { char buf[100]; Ns_Time currentTime; - + Ns_GetTime(¤tTime); Ns_MutexLock(&tp.queuelock); snprintf(buf, sizeof(buf), "queue_id_%lx_%" TCL_LL_MODIFIER "x", tp.nextQueueId++, (Tcl_WideInt) currentTime.sec); Ns_MutexUnlock(&tp.queuelock); - + Tcl_SetObjResult(interp, Tcl_NewStringObj(buf, -1)); } return result; @@ -1320,14 +1316,14 @@ JobGenIDObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_ * * JobThreadListObjCmd, subcommand of NsTclJobCmd -- * - * Implements the "ns_job threadlist" command. + * Implements the "ns_job threadlist" command. * Return a list of the thread pool's fields. * * Results: - * Standard Tcl result. + * Standard Tcl result. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ @@ -1373,13 +1369,13 @@ JobThreadListObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, * * NsTclJobCmd -- * - * Implement the ns_job command to manage background tasks. + * Implement the ns_job command to manage background tasks. * * Results: - * Standard Tcl result. + * Standard Tcl result. * * Side effects: - * Jobs may be queued to run in another thread. + * Jobs may be queued to run in another thread. * *---------------------------------------------------------------------- */ @@ -1414,13 +1410,13 @@ NsTclJobObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CON * * JobThread -- * - * Background thread for the ns_job command. + * Background thread for the ns_job command. * * Results: - * None. + * None. * * Side effects: - * Jobs will be run from the queue. + * Jobs will be run from the queue. * *---------------------------------------------------------------------- */ @@ -1448,16 +1444,16 @@ JobThread(void *UNUSED(arg)) /* * Setting this parameter to > 0 will cause the thread to - * graceously exit after processing that many job requests, + * graciously exit after processing that many job requests, * thus initiating kind-of Tcl-level garbage collection. */ jpt = njobs = tp.jobsPerThread; while (jpt == 0 || njobs > 0) { - Job *jobPtr; - Tcl_Interp *interp; - int code; + Job *jobPtr; + Tcl_Interp *interp; + int code; Ns_ReturnCode status; ++tp.nidle; @@ -1483,7 +1479,7 @@ JobThread(void *UNUSED(arg)) if (LookupQueue(NULL, jobPtr->queueId, &queue, NS_TRUE) != TCL_OK) { Ns_Log(Fatal, "cannot find queue: %s", jobPtr->queueId); } - assert(queue != NULL); + assert(queue != NULL); /* * Get an interpreter.... @@ -1501,7 +1497,7 @@ JobThread(void *UNUSED(arg)) if (jobPtr->cancel != 0) { Tcl_AsyncMark(jobPtr->async); } - + /* * ... rename the thread to job thread... */ @@ -1573,7 +1569,10 @@ JobThread(void *UNUSED(arg)) (void)ReleaseQueue(queue, NS_TRUE); if ((jpt != 0) && --njobs <= 0) { - break; /* Served given # of jobs in this thread */ + /* + * Served given # of jobs in this thread + */ + break; } } @@ -1612,7 +1611,10 @@ JobAbort(ClientData UNUSED(clientData), Tcl_Interp *interp, int UNUSED(code)) Ns_Log(Warning, "ns_job: job cancelled"); } - return TCL_ERROR; /* Forces current command error */ + /* + * Force current command error + */ + return TCL_ERROR; } /* @@ -1647,7 +1649,7 @@ GetNextJob(void) if (LookupQueue(NULL, jobPtr->queueId, &queue, NS_TRUE) != TCL_OK) { Ns_Log(Fatal, "cannot find queue: %s", jobPtr->queueId); } - assert(queue != NULL); + assert(queue != NULL); if (queue->nRunning < queue->maxThreads) { @@ -1683,13 +1685,13 @@ GetNextJob(void) * * NewQueue -- * - * Create a thread pool queue. + * Create a thread pool queue. * * Results: - * Thread pool queue. + * Thread pool queue. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ @@ -1704,10 +1706,8 @@ NewQueue(const char *queueName, const char *queueDesc, int maxThreads) queue = ns_calloc(1u, sizeof(Queue)); queue->req = QUEUE_REQ_NONE; - queue->name = ns_strdup(queueName); queue->desc = ns_strdup(queueDesc); - queue->maxThreads = maxThreads; queue->refCount = 0; @@ -1725,13 +1725,13 @@ NewQueue(const char *queueName, const char *queueDesc, int maxThreads) * * FreeQueue -- * - * Cleanup the queue + * Cleanup the queue * * Results: - * None. + * None. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ @@ -1754,13 +1754,13 @@ FreeQueue(Queue *queue) * * NewJob -- * - * Create a new job and initialize it. + * Create a new job and initialize it. * * Results: - * None. + * None. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ @@ -1780,7 +1780,6 @@ NewJob(const char* server, const char* queueName, JobTypes type, const char *scr jobPtr->state = JOB_SCHEDULED; jobPtr->code = TCL_OK; jobPtr->req = JOB_NONE; - jobPtr->queueId = ns_strdup(queueName); Tcl_DStringInit(&jobPtr->id); @@ -1797,13 +1796,13 @@ NewJob(const char* server, const char* queueName, JobTypes type, const char *scr * * FreeJob -- * - * Destroy a Job structure. + * Destroy a Job structure. * * Results: - * None. + * None. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ @@ -1843,7 +1842,7 @@ FreeJob(Job *jobPtr) * queue can be referenced then we will again need the refCount. * * Results: - * Stanard Tcl result. + * Standard Tcl result. * * Side effects: * None. @@ -1864,13 +1863,13 @@ LookupQueue(Tcl_Interp *interp, const char *queueName, Queue **queuePtr, Ns_MutexLock(&tp.queuelock); } - *queuePtr = NULL; - hPtr = Tcl_FindHashEntry(&tp.queues, queueName); if (hPtr != NULL) { *queuePtr = Tcl_GetHashValue(hPtr); Ns_MutexLock(&(*queuePtr)->lock); ++(*queuePtr)->refCount; + } else { + *queuePtr = NULL; } if (!locked) { @@ -1959,7 +1958,7 @@ ReleaseQueue(Queue *queue, bool locked) --queue->refCount; /* - * Delete the queue, honouring constraints + * Delete the queue, honoring constraints */ if (queue->req == QUEUE_REQ_DELETE @@ -2021,7 +2020,7 @@ AnyDone(Queue *queue) hPtr = Tcl_FirstHashEntry(&queue->jobs, &search); while (hPtr != NULL) { - const Job *jobPtr = Tcl_GetHashValue(hPtr); + const Job *jobPtr = Tcl_GetHashValue(hPtr); if (jobPtr->state == JOB_DONE) { result = NS_TRUE; @@ -2063,7 +2062,9 @@ GetJobCodeStr(int code) "UNKNOWN_CODE" /* 5 */ }; - /* Check the caller's input. */ + /* + * Check the caller's input and limit to the max. + */ if (code > max_code_index) { code = max_code_index; } @@ -2295,8 +2296,8 @@ AppendFieldInt(Tcl_Interp *interp, Tcl_Obj *list, const char *name, int value) NS_NONNULL_ASSERT(name != NULL); /* - * Note: If there occurs within Tcl_ListObjAppendElement it will - * set the result anyway. + * Note: If there occurs an error within Tcl_ListObjAppendElement + * it will set the result anyway. */ elObj = Tcl_NewStringObj(name, -1); diff --git a/nsd/tclmisc.c b/nsd/tclmisc.c index 5691a987..a27b4ed0 100644 --- a/nsd/tclmisc.c +++ b/nsd/tclmisc.c @@ -364,6 +364,8 @@ NsTclStripHtmlObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc /* * Make a copy of the input and point the moving and output ptrs to it. */ + assert(htmlString != NULL); + inString = ns_strdup(htmlString); inPtr = inString; outPtr = inString; @@ -499,9 +501,12 @@ NsTclHrefsObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tc } else { char *s, *e; - const char *p = htmlString; + const char *p; Tcl_Obj *listObj = Tcl_NewListObj(0, NULL); + + assert(htmlString != NULL); + p = htmlString; while (((s = strchr(p, INTCHAR('<'))) != NULL) && ((e = strchr(s, INTCHAR('>'))) != NULL)) { ++s; *e = '\0'; diff --git a/nsd/tclobj.c b/nsd/tclobj.c index 9fd4a84e..5110e33c 100644 --- a/nsd/tclobj.c +++ b/nsd/tclobj.c @@ -11,7 +11,7 @@ * * The Original Code is AOLserver Code and related documentation * distributed by AOL. - * + * * The Initial Developer of the Original Code is America Online, * Inc. Portions created by AOL are Copyright (C) 1999 America Online, * Inc. All Rights Reserved. @@ -56,15 +56,17 @@ static Tcl_ObjType addrType = { }; static const Tcl_ObjType *byteArrayTypePtr; /* For NsTclIsByteArray(). */ - +static const Tcl_ObjType *properByteArrayTypePtr; /* For NsTclIsByteArray(). */ /* *---------------------------------------------------------------------- * * NsTclInitAddrType -- * - * Initialize the Tcl address object type and cache the bytearray - * Tcl built-in type. + * Initialize the Tcl address object type and cache the bytearray Tcl + * built-in type. Starting with Tcl 8.7a1, Tcl has actually two different + * types for bytearrays, the old "tclByteArrayType" and a new + * "properByteArrayType", where both have the string name "bytearray". * * Results: * None. @@ -78,8 +80,21 @@ static const Tcl_ObjType *byteArrayTypePtr; /* For NsTclIsByteArray(). */ void NsTclInitAddrType(void) { + Tcl_Obj *newByteObj; + Tcl_RegisterObjType(&addrType); + /* + * Get the "tclByteArrayType" via name "bytearray". + */ byteArrayTypePtr = Tcl_GetObjType("bytearray"); + newByteObj = Tcl_NewByteArrayObj(NULL,0); + + /* + * Get the "properByteArrayType" via a TclObj. + * In versions before Tcl 8.7, both values will be the same. + */ + properByteArrayTypePtr = newByteObj->typePtr; + Tcl_DecrRefCount(newByteObj); } @@ -137,7 +152,7 @@ Ns_TclSetTwoPtrValue(Tcl_Obj *objPtr, Tcl_ObjType *newTypePtr, void *ptr1, void *ptr2) { NS_NONNULL_ASSERT(objPtr != NULL); - + Ns_TclResetObjType(objPtr, newTypePtr); objPtr->internalRep.twoPtrValue.ptr1 = ptr1; objPtr->internalRep.twoPtrValue.ptr2 = ptr2; @@ -167,7 +182,7 @@ Ns_TclSetOtherValuePtr(Tcl_Obj *objPtr, Tcl_ObjType *newTypePtr, void *value) NS_NONNULL_ASSERT(objPtr != NULL); NS_NONNULL_ASSERT(newTypePtr != NULL); NS_NONNULL_ASSERT(value != NULL); - + Ns_TclResetObjType(objPtr, newTypePtr); objPtr->internalRep.otherValuePtr = value; } @@ -196,7 +211,7 @@ Ns_TclSetStringRep(Tcl_Obj *objPtr, const char *bytes, int length) { NS_NONNULL_ASSERT(objPtr != NULL); NS_NONNULL_ASSERT(bytes != NULL); - + if (length < 1) { length = (int)strlen(bytes); } @@ -257,18 +272,18 @@ Ns_TclGetAddrFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, const char *type, void **addrPtrPtr) { int result = TCL_OK; - + NS_NONNULL_ASSERT(objPtr != NULL); NS_NONNULL_ASSERT(type != NULL); NS_NONNULL_ASSERT(addrPtrPtr != NULL); - + if (Tcl_ConvertToType(interp, objPtr, &addrType) != TCL_OK) { result = TCL_ERROR; - + } else if (objPtr->internalRep.twoPtrValue.ptr1 != (void *) type) { Ns_TclPrintfResult(interp, "incorrect type: %s", Tcl_GetString(objPtr)); result = TCL_ERROR; - + } else { *addrPtrPtr = objPtr->internalRep.twoPtrValue.ptr2; } @@ -299,7 +314,7 @@ Ns_TclSetAddrObj(Tcl_Obj *objPtr, const char *type, void *addr) NS_NONNULL_ASSERT(objPtr != NULL); NS_NONNULL_ASSERT(type != NULL); NS_NONNULL_ASSERT(addr != NULL); - + if (Tcl_IsShared(objPtr)) { Tcl_Panic("Ns_TclSetAddrObj called with shared object"); } @@ -328,11 +343,11 @@ int Ns_TclGetOpaqueFromObj(const Tcl_Obj *objPtr, const char *type, void **addrPtrPtr) { int result = TCL_OK; - + NS_NONNULL_ASSERT(objPtr != NULL); NS_NONNULL_ASSERT(type != NULL); NS_NONNULL_ASSERT(addrPtrPtr != NULL); - + if (objPtr->typePtr != &addrType || objPtr->internalRep.twoPtrValue.ptr1 != (void *) type) { result = TCL_ERROR; @@ -367,7 +382,7 @@ Ns_TclSetOpaqueObj(Tcl_Obj *objPtr, const char *type, void *addr) { NS_NONNULL_ASSERT(objPtr != NULL); NS_NONNULL_ASSERT(type != NULL); - + Ns_TclSetTwoPtrValue(objPtr, &addrType, (void *) type, addr); } @@ -379,8 +394,7 @@ Ns_TclSetOpaqueObj(Tcl_Obj *objPtr, const char *type, void *addr) * * Does the given Tcl object have a byte array internal rep? The * function determines when it is safe to interpret a string as a - * byte array directly. It is the same as Tcl 8.6's - * TclIsPureByteArray(Tcl_Obj *objPtr) + * byte array directly. * * Results: * Boolean. @@ -395,8 +409,10 @@ bool NsTclObjIsByteArray(const Tcl_Obj *objPtr) { NS_NONNULL_ASSERT(objPtr != NULL); - - return (objPtr->typePtr == byteArrayTypePtr) ? NS_TRUE : NS_FALSE; + + return ((objPtr->typePtr == properByteArrayTypePtr) + || (objPtr->typePtr == byteArrayTypePtr) + ) ? NS_TRUE : NS_FALSE; } @@ -407,7 +423,7 @@ NsTclObjIsByteArray(const Tcl_Obj *objPtr) * * Update the string representation for an address object. * Note: This procedure does not free an existing old string rep - * so storage will be lost if this has not already been done. + * so storage will be lost if this has not already been done. * * Results: * None. diff --git a/nsd/tclresp.c b/nsd/tclresp.c index a9f7a80f..f3b9ddf8 100644 --- a/nsd/tclresp.c +++ b/nsd/tclresp.c @@ -107,11 +107,11 @@ NsTclHeadersObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj } else if (binary != 0) { conn->flags |= NS_CONN_WRITE_ENCODED; } - + if (length > -1) { Ns_ConnSetLengthHeader(conn, (size_t)length, NS_FALSE); } - + /* * Request HTTP headers from ns_write etc. */ @@ -167,9 +167,9 @@ NsTclStartContentObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl } else if (charset != NULL && type != NULL) { Ns_TclPrintfResult(interp, "only one of -charset or -type may be specified"); result = TCL_ERROR; - + } else { - + Ns_LogDeprecated(objv, 1, "ns_headers ...", NULL); itPtr->nsconn.flags |= CONN_TCLHTTP; @@ -182,11 +182,11 @@ NsTclStartContentObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl } } if (result == TCL_OK) { - + if (type != NULL) { encoding = Ns_GetTypeEncoding(type); } - + if (encoding != NULL) { Ns_ConnSetEncoding(conn, encoding); } @@ -230,10 +230,10 @@ NsTclWriteObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *C if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "data ?data ...?"); result = TCL_ERROR; - + } else if (NsConnRequire(interp, &conn) != NS_OK) { result = TCL_ERROR; - + } else if (Ns_ConnSockPtr(conn) == NULL) { Ns_TclPrintfResult(interp, "connection channels is detached"); result = TCL_ERROR; @@ -357,7 +357,7 @@ NsTclReturnObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, T || NsConnRequire(interp, &conn) != NS_OK ) { result = TCL_ERROR; - + } else { const char *data; @@ -414,7 +414,7 @@ NsTclRespondObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, {"-binary", Ns_ObjvByteArray, &binary, &length}, {NULL, NULL, NULL, NULL} }; - + if (Ns_ParseObjv(opts, NULL, interp, 1, objc, objv) != NS_OK || NsConnRequire(interp, &conn) != NS_OK ) { @@ -456,26 +456,26 @@ NsTclRespondObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, } else { status = Ns_ConnReturnOpenChannel(conn, httpStatus, type, chan, (size_t)length); } - + } else if (filename != NULL) { /* * We'll be returning a file by name */ status = Ns_ConnReturnFile(conn, httpStatus, type, filename); - + } else if (binary != NULL) { /* * We'll be returning a binary data */ status = Ns_ConnReturnData(conn, httpStatus, binary, length, type); - + } else { /* * We'll be returning chars. */ status = Ns_ConnReturnCharData(conn, httpStatus, chars, length, type); } - + result = Result(interp, status); } return result; @@ -513,7 +513,7 @@ NsTclReturnFileObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int obj {"filename", Ns_ObjvString, &fileName, NULL}, {NULL, NULL, NULL, NULL} }; - + if (Ns_ParseObjv(NULL, args, interp, 1, objc, objv) != NS_OK || NsConnRequire(interp, &conn) != NS_OK ) { @@ -522,7 +522,7 @@ NsTclReturnFileObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int obj } else { result = Result(interp, Ns_ConnReturnFile(conn, httpStatus, mimeType, fileName)); } - + return result; } @@ -604,7 +604,7 @@ NsTclConnSendFpObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int obj {"len", Ns_ObjvInt, &len, NULL}, {NULL, NULL, NULL, NULL} }; - + if (Ns_ParseObjv(NULL, args, interp, 1, objc, objv) != NS_OK || NsConnRequire(interp, &conn) != NS_OK ) { @@ -614,12 +614,12 @@ NsTclConnSendFpObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int obj result = Ns_TclGetOpenChannel(interp, channelName, 0, NS_TRUE, &chan); if (likely( result == TCL_OK )) { Ns_ReturnCode status; - + Ns_LogDeprecated(objv, 3, "ns_writefp fileid ?nbytes?", NULL); - + conn->flags |= NS_CONN_SKIPHDRS; status = Ns_ConnSendChannel(conn, chan, (size_t)len); - + if (status != NS_OK) { Ns_TclPrintfResult(interp, "could not send %d bytes from channel %s", len, channelName); @@ -659,14 +659,14 @@ NsTclReturnBadRequestObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, i if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "reason"); result = TCL_ERROR; - + } else if (NsConnRequire(interp, &conn) != NS_OK) { result = TCL_ERROR; } else { result = Result(interp, Ns_ConnReturnBadRequest(conn, Tcl_GetString(objv[1]))); } - + return result; } @@ -690,9 +690,9 @@ NsTclReturnBadRequestObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, i */ static int -ReturnObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, - int UNUSED(objc), Tcl_Obj *CONST* UNUSED(objv), - Ns_ReturnCode (*proc) (Ns_Conn *conn)) +ReturnObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, + int UNUSED(objc), Tcl_Obj *CONST* UNUSED(objv), + Ns_ReturnCode (*proc) (Ns_Conn *conn)) { Ns_Conn *conn = NULL; int result; @@ -760,14 +760,14 @@ int NsTclReturnErrorObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) { int httpStatus, result; - Ns_Conn *conn = NULL; + Ns_Conn *conn = NULL; char *message; Ns_ObjvSpec args[] = { {"status", Ns_ObjvInt, &httpStatus, NULL}, {"message", Ns_ObjvString, &message, NULL}, {NULL, NULL, NULL, NULL} }; - + if (Ns_ParseObjv(NULL, args, interp, 1, objc, objv) != NS_OK || NsConnRequire(interp, &conn) != NS_OK ) { @@ -893,7 +893,7 @@ NsTclReturnRedirectObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int } else { result = Result(interp, Ns_ConnReturnRedirect(conn, location)); } - + return result; } @@ -931,7 +931,7 @@ NsTclInternalRedirectObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, i } else { result = Result(interp, Ns_ConnRedirect(conn, location)); - } + } return result; } @@ -957,7 +957,7 @@ static int Result(Tcl_Interp *interp, Ns_ReturnCode result) { NS_NONNULL_ASSERT(interp != NULL); - + Tcl_SetObjResult(interp, Tcl_NewBooleanObj((result == NS_OK) ? 1 : 0)); return TCL_OK; } diff --git a/nsd/tclset.c b/nsd/tclset.c index 0d65fedc..3b51d0a2 100644 --- a/nsd/tclset.c +++ b/nsd/tclset.c @@ -49,7 +49,7 @@ * Local functions defined in this file */ -static int LookupSet(NsInterp *itPtr, CONST char *id, bool deleteEntry, Ns_Set **setPtr) +static int LookupSet(NsInterp *itPtr, const char *id, bool deleteEntry, Ns_Set **setPtr) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(4); static int LookupObjSet(NsInterp *itPtr, Tcl_Obj *idPtr, bool deleteEntry, Ns_Set **setPtr) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(4); @@ -744,30 +744,30 @@ EnterSet(NsInterp *itPtr, Ns_Set *set, Ns_TclSetType type) { Tcl_HashTable *tablePtr; Tcl_HashEntry *hPtr; - int isNew; - int next; - char typeChar; + int isNew, len; + uint32_t next; char buf[TCL_INTEGER_SPACE + 1]; NS_NONNULL_ASSERT(itPtr != NULL); NS_NONNULL_ASSERT(set != NULL); tablePtr = &itPtr->sets; - typeChar = (type == NS_TCL_SET_DYNAMIC) ? SET_DYNAMIC : SET_STATIC; + buf[0] = (type == NS_TCL_SET_DYNAMIC) ? SET_DYNAMIC : SET_STATIC; /* * Allocate a new set IDs until we find an unused one. */ - - next = tablePtr->numEntries; - do { - snprintf(buf, sizeof(buf), "%c%d", typeChar, next); - ++next; + for (next = (uint32_t)tablePtr->numEntries; ; ++ next) { + len = ns_uint32toa(buf+1, next); hPtr = Tcl_CreateHashEntry(tablePtr, buf, &isNew); - } while (isNew == 0); + if (isNew != 0) { + break; + } + } Tcl_SetHashValue(hPtr, set); - return Tcl_NewStringObj(buf, -1); + + return Tcl_NewStringObj(buf, len+1); } @@ -820,7 +820,7 @@ LookupInterpSet(Tcl_Interp *interp, const char *id, bool deleteEntry, Ns_Set **s } static int -LookupSet(NsInterp *itPtr, CONST char *id, bool deleteEntry, Ns_Set **setPtr) +LookupSet(NsInterp *itPtr, const char *id, bool deleteEntry, Ns_Set **setPtr) { Tcl_HashEntry *hPtr; Ns_Set *set = NULL; diff --git a/nsd/tclsock.c b/nsd/tclsock.c index c7e6a91d..d059ef3c 100644 --- a/nsd/tclsock.c +++ b/nsd/tclsock.c @@ -922,14 +922,14 @@ NsTclSockListenCallbackObjCmd(ClientData clientData, Tcl_Interp *interp, int obj assert(script != NULL); if (STREQ(addr, "*")) { - addr = NULL; + addr = (char *)NS_IP_UNSPECIFIED; } scriptLength = strlen(script); lcbPtr = ns_malloc(sizeof(ListenCallback) + scriptLength); lcbPtr->server = (itPtr->servPtr != NULL ? itPtr->servPtr->server : NULL); memcpy(lcbPtr->script, script, scriptLength + 1u); - if (Ns_SockListenCallback(addr, port, SockListenCallback, lcbPtr) != NS_OK) { + if (Ns_SockListenCallback(addr, port, SockListenCallback, NS_FALSE, lcbPtr) == NS_INVALID_SOCKET) { Ns_TclPrintfResult(interp, "could not register callback"); ns_free(lcbPtr); result = TCL_ERROR; diff --git a/nsd/tclthread.c b/nsd/tclthread.c index 7d9266a3..780318b1 100644 --- a/nsd/tclthread.c +++ b/nsd/tclthread.c @@ -1001,7 +1001,7 @@ CreateSynchObject(const NsInterp *itPtr, Ns_DStringInit(&ds); do { - Ns_DStringTrunc(&ds, 0); + Ns_DStringSetLength(&ds, 0); Ns_DStringPrintf(&ds, "%s:tcl:%u", type, (*idPtr)++); hPtr = Tcl_CreateHashEntry(typeTable, ds.string, &isNew); } while (isNew == 0); diff --git a/nsd/tcltime.c b/nsd/tcltime.c index f4e80c10..45abdbc3 100644 --- a/nsd/tcltime.c +++ b/nsd/tcltime.c @@ -519,13 +519,17 @@ NsTclSleepObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tc if (Ns_ParseObjv(NULL, args, interp, 1, objc, objv) != NS_OK) { rc = TCL_ERROR; - } else if (tPtr->sec < 0 || (tPtr->sec == 0 && tPtr->usec < 0)) { - Ns_TclPrintfResult(interp, "invalid timespec: %s", Tcl_GetString(objv[1])); - rc = TCL_ERROR; } else { - int ms = (int)(tPtr->sec * 1000 + tPtr->usec / 1000); - - Tcl_Sleep(ms); + assert(tPtr != NULL); + + if (tPtr->sec < 0 || (tPtr->sec == 0 && tPtr->usec < 0)) { + Ns_TclPrintfResult(interp, "invalid timespec: %s", Tcl_GetString(objv[1])); + rc = TCL_ERROR; + } else { + int ms = (int)(tPtr->sec * 1000 + tPtr->usec / 1000); + + Tcl_Sleep(ms); + } } return rc; @@ -618,7 +622,7 @@ UpdateStringOfTime(Tcl_Obj *objPtr) timePtr = (Ns_Time *) (void *) &objPtr->internalRep; Ns_AdjTime(timePtr); if (timePtr->usec == 0) { - len = snprintf(buf, sizeof(buf), "%ld", timePtr->sec); + len = ns_uint64toa(buf, (uint64_t)timePtr->sec); } else { len = snprintf(buf, sizeof(buf), "%ld:%ld", timePtr->sec, timePtr->usec); diff --git a/nsd/tclvar.c b/nsd/tclvar.c index 1de1855f..11ead9c1 100644 --- a/nsd/tclvar.c +++ b/nsd/tclvar.c @@ -11,7 +11,7 @@ * * The Original Code is AOLserver Code and related documentation * distributed by AOL. - * + * * The Initial Developer of the Original Code is America Online, * Inc. Portions created by AOL are Copyright (C) 1999 America Online, * Inc. All Rights Reserved. @@ -89,7 +89,7 @@ static Array *LockArrayObj(Tcl_Interp *interp, Tcl_Obj *arrayObj, bool create) static Array *GetArray(Bucket *bucketPtr, const char *arrayName, bool create) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2); -static unsigned int BucketIndex(const char *arrayName) +static unsigned int BucketIndex(const char *arrayName) NS_GNUC_NONNULL(1); /* @@ -117,8 +117,9 @@ NsTclCreateBuckets(const char *server, int nbuckets) NS_NONNULL_ASSERT(server != NULL); buckets = ns_malloc(sizeof(Bucket) * (size_t)nbuckets); + memcpy(buf, "nsv:", 4); while (--nbuckets >= 0) { - snprintf(buf, sizeof(buf), "nsv:%d", nbuckets); + (void) ns_uint32toa(&buf[4], (uint32_t)nbuckets); Tcl_InitHashTable(&buckets[nbuckets].arrays, TCL_STRING_KEYS); buckets[nbuckets].lock = NULL; Ns_MutexInit(&buckets[nbuckets].lock); @@ -157,7 +158,7 @@ NsTclNsvGetObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, } else { Array *arrayPtr = LockArrayObj(interp, objv[1], NS_FALSE); - + if (unlikely(arrayPtr == NULL)) { result = TCL_ERROR; @@ -165,7 +166,7 @@ NsTclNsvGetObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, Tcl_Obj *resultObj; const Tcl_HashEntry *hPtr; - hPtr = Tcl_FindHashEntry(&arrayPtr->vars, Tcl_GetString(objv[2])); + hPtr = Tcl_CreateHashEntry(&arrayPtr->vars, Tcl_GetString(objv[2]), NULL); resultObj = likely(hPtr != NULL) ? Tcl_NewStringObj(Tcl_GetHashValue(hPtr), -1) : NULL; UnlockArray(arrayPtr); @@ -211,7 +212,7 @@ NsTclNsvExistsObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) { int result; - + if (unlikely(objc != 3)) { Tcl_WrongNumArgs(interp, 1, objv, "array key"); result = TCL_ERROR; @@ -220,8 +221,8 @@ NsTclNsvExistsObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, Array *arrayPtr = LockArrayObj(interp, objv[1], NS_FALSE); if (likely(arrayPtr != NULL)) { - if (Tcl_FindHashEntry(&arrayPtr->vars, - Tcl_GetString(objv[2])) != NULL) { + if (Tcl_CreateHashEntry(&arrayPtr->vars, + Tcl_GetString(objv[2]), NULL) != NULL) { exists = NS_TRUE; } UnlockArray(arrayPtr); @@ -282,7 +283,7 @@ NsTclNsvSetObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, } else { const Tcl_HashEntry *hPtr; - hPtr = Tcl_FindHashEntry(&arrayPtr->vars, key); + hPtr = Tcl_CreateHashEntry(&arrayPtr->vars, key, NULL); if (likely(hPtr != NULL)) { Tcl_SetObjResult(interp, Tcl_NewStringObj(Tcl_GetHashValue(hPtr), -1)); } else { @@ -329,7 +330,7 @@ NsTclNsvIncrObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, } else { Tcl_WideInt current; Array *arrayPtr = LockArrayObj(interp, objv[1], NS_TRUE); - + assert(arrayPtr != NULL); result = IncrVar(arrayPtr, Tcl_GetString(objv[2]), count, ¤t); UnlockArray(arrayPtr); @@ -352,10 +353,10 @@ NsTclNsvIncrObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, * Implements nsv_lappend command. * * Results: - * Tcl result. + * Tcl result. * * Side effects: - * See docs. + * See docs. * *----------------------------------------------------------------------------- */ @@ -375,7 +376,7 @@ NsTclNsvLappendObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, const char *value; int isNew, len; Tcl_Obj *listObj; - + arrayPtr = LockArrayObj(interp, objv[1], NS_TRUE); assert(arrayPtr != NULL); @@ -393,7 +394,7 @@ NsTclNsvLappendObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, value = Tcl_GetStringFromObj(listObj, &len); UpdateVar(hPtr, value, (size_t)len); UnlockArray(arrayPtr); - + Tcl_SetObjResult(interp, listObj); } return result; @@ -408,10 +409,10 @@ NsTclNsvLappendObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, * Implements nsv_append command. * * Results: - * Tcl result. + * Tcl result. * * Side effects: - * See docs. + * See docs. * *----------------------------------------------------------------------------- */ @@ -431,7 +432,7 @@ NsTclNsvAppendObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, const char *value; int i, isNew, len; Tcl_Obj *resultObj; - + arrayPtr = LockArrayObj(interp, objv[1], NS_TRUE); assert(arrayPtr != NULL); @@ -458,13 +459,13 @@ NsTclNsvAppendObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, * * NsTclNsvUnsetObjCmd -- * - * Implements nsv_unset as an obj command. + * Implements nsv_unset as an obj command. * * Results: - * Tcl result. + * Tcl result. * * Side effects: - * See docs. + * See docs. * *----------------------------------------------------------------------------- */ @@ -486,22 +487,18 @@ NsTclNsvUnsetObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, {"?key", Ns_ObjvString, &key, NULL}, {NULL, NULL, NULL, NULL} }; - + if (Ns_ParseObjv(opts, args, interp, 1, objc, objv) != NS_OK) { result = TCL_ERROR; } else { Array *arrayPtr = LockArrayObj(interp, arrayObj, NS_FALSE); - + if (unlikely(arrayPtr == NULL)) { - if (nocomplain != 0) { - Tcl_ResetResult(interp); - } else { - result = TCL_ERROR; - } - + result = TCL_ERROR; + } else { - + assert(arrayPtr != NULL); if (Unset(arrayPtr, key) != NS_OK && key != NULL) { @@ -522,7 +519,7 @@ NsTclNsvUnsetObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, Tcl_DeleteHashEntry(arrayPtr->entryPtr); } UnlockArray(arrayPtr); - + if (result == TCL_OK && key == NULL) { /* * Free the actual array data strucure and invalidate the @@ -532,7 +529,13 @@ NsTclNsvUnsetObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, Ns_TclSetTwoPtrValue(arrayObj, NULL, NULL, NULL); } } - } + } + + if (nocomplain != 0) { + Tcl_ResetResult(interp); + result = TCL_OK; + } + return result; } @@ -568,7 +571,7 @@ NsTclNsvNamesObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj Tcl_Obj *resultObj; const char *pattern; int i; - + pattern = (objc < 2) ? NULL : Tcl_GetString(objv[1]); /* @@ -580,12 +583,12 @@ NsTclNsvNamesObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj const Tcl_HashEntry *hPtr; Tcl_HashSearch search; Bucket *bucketPtr = &servPtr->nsv.buckets[i]; - + Ns_MutexLock(&bucketPtr->lock); hPtr = Tcl_FirstHashEntry(&bucketPtr->arrays, &search); while (hPtr != NULL) { const char *key = Tcl_GetHashKey(&bucketPtr->arrays, hPtr); - + if ((pattern == NULL) || (Tcl_StringMatch(key, pattern) != 0)) { result = Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj(key, -1)); @@ -646,7 +649,7 @@ NsTclNsvArrayObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int lobjc, size; Array *arrayPtr; Tcl_Obj **lobjv; - + switch (opt) { case CSetIdx: /* fall through */ case CResetIdx: @@ -666,7 +669,7 @@ NsTclNsvArrayObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, arrayPtr = LockArrayObj(interp, objv[2], NS_TRUE); assert(arrayPtr != NULL); - + if (opt == (int)CResetIdx) { Flush(arrayPtr); } @@ -721,7 +724,7 @@ NsTclNsvArrayObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, } else { Tcl_HashSearch search; - + arrayPtr = LockArrayObj(interp, objv[2], NS_FALSE); Tcl_ResetResult(interp); if (arrayPtr != NULL) { @@ -788,13 +791,13 @@ Ns_VarGet(const char *server, const char *array, const char *key, Ns_DString *ds if (likely(servPtr != NULL)) { Array *arrayPtr = LockArray(servPtr, array, NS_FALSE); if (likely(arrayPtr != NULL)) { - const Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&arrayPtr->vars, key); - if (likely(hPtr != NULL)) { - Ns_DStringAppend(dsPtr, Tcl_GetHashValue(hPtr)); - status = NS_OK; - } - UnlockArray(arrayPtr); - } + const Tcl_HashEntry *hPtr = Tcl_CreateHashEntry(&arrayPtr->vars, key, NULL); + if (likely(hPtr != NULL)) { + Ns_DStringAppend(dsPtr, Tcl_GetHashValue(hPtr)); + status = NS_OK; + } + UnlockArray(arrayPtr); + } } return status; } @@ -827,14 +830,14 @@ Ns_VarExists(const char *server, const char *array, const char *key) servPtr = NsGetServer(server); if (likely(servPtr != NULL)) { - Array *arrayPtr = LockArray(servPtr, array, NS_FALSE); - + Array *arrayPtr = LockArray(servPtr, array, NS_FALSE); + if (likely(arrayPtr != NULL)) { - if (Tcl_FindHashEntry(&arrayPtr->vars, key) != NULL) { - exists = NS_TRUE; - } - UnlockArray(arrayPtr); - } + if (Tcl_CreateHashEntry(&arrayPtr->vars, key, NULL) != NULL) { + exists = NS_TRUE; + } + UnlockArray(arrayPtr); + } } return exists; } @@ -869,13 +872,13 @@ Ns_VarSet(const char *server, const char *array, const char *key, servPtr = NsGetServer(server); if (likely(servPtr != NULL)) { - Array *arrayPtr = LockArray(servPtr, array, NS_TRUE); - - if (likely(arrayPtr != NULL)) { - SetVar(arrayPtr, key, value, (len > -1) ? (size_t)len : strlen(value)); - UnlockArray(arrayPtr); - status = NS_OK; - } + Array *arrayPtr = LockArray(servPtr, array, NS_TRUE); + + if (likely(arrayPtr != NULL)) { + SetVar(arrayPtr, key, value, (len > -1) ? (size_t)len : strlen(value)); + UnlockArray(arrayPtr); + status = NS_OK; + } } return status; } @@ -892,7 +895,7 @@ Ns_VarSet(const char *server, const char *array, const char *key, * The new value of the counter. * * Side effects: - * Missing keys are initialised to 1. + * Missing keys are initialized to 1. * *----------------------------------------------------------------------------- */ @@ -908,12 +911,12 @@ Ns_VarIncr(const char *server, const char *array, const char *key, int incr) servPtr = NsGetServer(server); if (likely(servPtr != NULL)) { - Array *arrayPtr = LockArray(servPtr, array, NS_TRUE); - - if (likely(arrayPtr != NULL)) { - (void) IncrVar(arrayPtr, key, incr, &counter); - UnlockArray(arrayPtr); - } + Array *arrayPtr = LockArray(servPtr, array, NS_TRUE); + + if (likely(arrayPtr != NULL)) { + (void) IncrVar(arrayPtr, key, incr, &counter); + UnlockArray(arrayPtr); + } } return counter; } @@ -949,26 +952,26 @@ Ns_VarAppend(const char *server, const char *array, const char *key, servPtr = NsGetServer(server); if (likely(servPtr != NULL)) { - Array *arrayPtr = LockArray(servPtr, array, NS_TRUE); + Array *arrayPtr = LockArray(servPtr, array, NS_TRUE); if (likely(arrayPtr != NULL)) { - Tcl_HashEntry *hPtr; - size_t oldLen, newLen; - char *oldString, *newString; + Tcl_HashEntry *hPtr; + size_t oldLen, newLen; + char *oldString, *newString; + + hPtr = Tcl_CreateHashEntry(&arrayPtr->vars, key, &isNew); - hPtr = Tcl_CreateHashEntry(&arrayPtr->vars, key, &isNew); + oldString = Tcl_GetHashValue(hPtr); + oldLen = (oldString != NULL) ? strlen(oldString) : 0u; - oldString = Tcl_GetHashValue(hPtr); - oldLen = (oldString != NULL) ? strlen(oldString) : 0u; + newLen = oldLen + ((len > -1) ? (size_t)len : strlen(value)) + 1u; + newString = ns_realloc(oldString, newLen + 1u); + memcpy(newString + oldLen, value, newLen + 1u); - newLen = oldLen + ((len > -1) ? (size_t)len : strlen(value)) + 1u; - newString = ns_realloc(oldString, newLen + 1u); - memcpy(newString + oldLen, value, newLen + 1u); + Tcl_SetHashValue(hPtr, newString); - Tcl_SetHashValue(hPtr, newString); - - UnlockArray(arrayPtr); - status = NS_OK; - } + UnlockArray(arrayPtr); + status = NS_OK; + } } return status; @@ -1002,11 +1005,11 @@ Ns_VarUnset(const char *server, const char *array, const char *key) servPtr = NsGetServer(server); if (likely(servPtr != NULL)) { - Array *arrayPtr = LockArray(servPtr, array, NS_FALSE); + Array *arrayPtr = LockArray(servPtr, array, NS_FALSE); if (likely(arrayPtr != NULL)) { - status = Unset(arrayPtr, key); - UnlockArray(arrayPtr); - } + status = Unset(arrayPtr, key); + UnlockArray(arrayPtr); + } } return status; } @@ -1034,8 +1037,8 @@ BucketIndex(const char *arrayName) { unsigned int index = 0u; for (;;) { - register unsigned int i = UCHAR(*(arrayName++)); - if (unlikely(i == 0u)) { + register unsigned int i = UCHAR(*(arrayName++)); + if (unlikely(i == 0u)) { break; } index += (index << 3u) + i; @@ -1068,23 +1071,24 @@ GetArray(Bucket *bucketPtr, const char *arrayName, bool create) { NS_NONNULL_ASSERT(bucketPtr != NULL); NS_NONNULL_ASSERT(arrayName != NULL); - + if (unlikely(create)) { int isNew; - + hPtr = Tcl_CreateHashEntry(&bucketPtr->arrays, arrayName, &isNew); if (isNew == 0) { arrayPtr = Tcl_GetHashValue(hPtr); } else { arrayPtr = ns_malloc(sizeof(Array)); - arrayPtr->locks = 0; + arrayPtr->locks = 0; arrayPtr->bucketPtr = bucketPtr; arrayPtr->entryPtr = hPtr; Tcl_InitHashTable(&arrayPtr->vars, TCL_STRING_KEYS); Tcl_SetHashValue(hPtr, arrayPtr); } } else { - hPtr = Tcl_FindHashEntry(&bucketPtr->arrays, arrayName); + + hPtr = Tcl_CreateHashEntry(&bucketPtr->arrays, arrayName, NULL); if (unlikely(hPtr == NULL)) { Ns_MutexUnlock(&bucketPtr->lock); return NULL; @@ -1125,7 +1129,7 @@ LockArray(const NsServer *servPtr, const char *arrayName, bool create) index = BucketIndex(arrayName); bucketPtr = &servPtr->nsv.buckets[index % (unsigned int)servPtr->nsv.nbuckets]; Ns_MutexLock(&bucketPtr->lock); - + return GetArray(bucketPtr, arrayName, create); } @@ -1220,7 +1224,6 @@ static int IncrVar(Array *arrayPtr, const char *key, int incr, Tcl_WideInt *valuePtr) { Tcl_HashEntry *hPtr; - const char *oldString; int isNew, status; Tcl_WideInt counter = -1; @@ -1229,14 +1232,17 @@ IncrVar(Array *arrayPtr, const char *key, int incr, Tcl_WideInt *valuePtr) NS_NONNULL_ASSERT(valuePtr != NULL); hPtr = Tcl_CreateHashEntry(&arrayPtr->vars, key, &isNew); - oldString = Tcl_GetHashValue(hPtr); if (isNew != 0) { counter = 0; status = TCL_OK; } else { + const char *oldString; + + oldString = Tcl_GetHashValue(hPtr); status = (Ns_StrToWideInt(oldString, &counter) == NS_OK) - ? TCL_OK : TCL_ERROR; + ? TCL_OK + : TCL_ERROR; } if (status == TCL_OK) { @@ -1276,7 +1282,7 @@ Unset(Array *arrayPtr, const char *key) NS_NONNULL_ASSERT(arrayPtr != NULL); if (key != NULL) { - Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&arrayPtr->vars, key); + Tcl_HashEntry *hPtr = Tcl_CreateHashEntry(&arrayPtr->vars, key, NULL); if (hPtr != NULL) { ns_free(Tcl_GetHashValue(hPtr)); @@ -1368,7 +1374,7 @@ LockArrayObj(Tcl_Interp *interp, Tcl_Obj *arrayObj, bool create) Ns_TclSetOpaqueObj(arrayObj, arrayType, arrayPtr->bucketPtr); } } - + if (arrayPtr == NULL && !create) { Ns_TclPrintfResult(interp, "no such array: %s", arrayName); } @@ -1403,23 +1409,23 @@ NsTclNsvBucketObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Ob { const NsInterp *itPtr = clientData; const NsServer *servPtr = itPtr->servPtr; - int bucketNr = -1, i, result = TCL_OK; + int bucketNr = -1, i, result = TCL_OK; if (objc > 2) { Tcl_WrongNumArgs(interp, 1, objv, "?bucket-number?"); - result = TCL_ERROR; + result = TCL_ERROR; - } else if (objc == 2 && - (Tcl_GetIntFromObj(interp, objv[1], &bucketNr) != TCL_OK - || bucketNr < 0 - || bucketNr >= servPtr->nsv.nbuckets - )) { + } else if (objc == 2 && + (Tcl_GetIntFromObj(interp, objv[1], &bucketNr) != TCL_OK + || bucketNr < 0 + || bucketNr >= servPtr->nsv.nbuckets + )) { Ns_TclPrintfResult(interp, "bucket number is not a valid integer"); result = TCL_ERROR; } else { Tcl_Obj *resultObj; - + /* LOCK for servPtr->nsv ? */ resultObj = Tcl_GetObjResult(interp); for (i = 0; i < servPtr->nsv.nbuckets; i++) { @@ -1427,7 +1433,7 @@ NsTclNsvBucketObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Ob Tcl_Obj *listObj; Tcl_HashSearch search; Bucket *bucketPtr; - + if (bucketNr > -1 && i != bucketNr) { continue; } diff --git a/nsd/tclxkeylist.c b/nsd/tclxkeylist.c index f86cbbe0..2e03ab00 100644 --- a/nsd/tclxkeylist.c +++ b/nsd/tclxkeylist.c @@ -268,9 +268,9 @@ Tcl_GetKeyedListField(Tcl_Interp *interp, const char *fieldName, } } else if (status == TCL_OK) { if (fieldValuePtr != NULL) { - int valueLen; + int valueLen; const char *keyValue = Tcl_GetStringFromObj(objValPtr, &valueLen); - char *newValue = ns_strncopy(keyValue, (ssize_t)valueLen); + char *newValue = ns_strncopy(keyValue, (ssize_t)valueLen); *fieldValuePtr = newValue; } diff --git a/nsd/url.c b/nsd/url.c index 17769a5f..fa4c01b2 100644 --- a/nsd/url.c +++ b/nsd/url.c @@ -11,7 +11,7 @@ * * The Original Code is AOLserver Code and related documentation * distributed by AOL. - * + * * The Initial Developer of the Original Code is America Online, * Inc. Portions created by AOL are Copyright (C) 1999 America Online, * Inc. All Rights Reserved. @@ -28,7 +28,7 @@ */ -/* +/* * url.c -- * * Parse URLs. @@ -42,13 +42,13 @@ * * Ns_RelativeUrl -- * - * If the url passed in is for this server, then the initial - * part of the URL is stripped off. e.g., on a server whose - * location is http://www.foo.com, Ns_RelativeUrl of - * "http://www.foo.com/hello" will return "/hello". + * If the url passed in is for this server, then the initial + * part of the URL is stripped off. e.g., on a server whose + * location is http://www.foo.com, Ns_RelativeUrl of + * "http://www.foo.com/hello" will return "/hello". * * Results: - * A pointer to the beginning of the relative url in the + * A pointer to the beginning of the relative url in the * passed-in url, or NULL if error. * * Side effects: @@ -93,13 +93,13 @@ Ns_RelativeUrl(const char *url, const char *location) * * Ns_ParseUrl -- * - * Parse a URL into its component parts + * Parse a URL into its component parts * * Results: - * NS_OK or NS_ERROR + * NS_OK or NS_ERROR * * Side effects: - * Pointers to the protocol, host, port, path, and "tail" (last + * Pointers to the protocol, host, port, path, and "tail" (last * path element) will be set by reference in the passed-in pointers. * The passed-in url will be modified. * @@ -119,7 +119,7 @@ Ns_ParseUrl(char *url, char **pprotocol, char **phost, *ptail = NULL; /* - * Set end to the end of the protocol + * Set variable "end" to the end of the protocol * http://www.foo.com:8000/baz/blah/spoo.html * ^ * +--end @@ -140,12 +140,12 @@ Ns_ParseUrl(char *url, char **pprotocol, char **phost, * | +-- end * +-------- *pprotocol */ - + *end = '\0'; *pprotocol = url; url = end + 1; } - + if (url[0] == '/' && url[1] == '/') { /* @@ -185,7 +185,7 @@ Ns_ParseUrl(char *url, char **pprotocol, char **phost, *pport = url; } else { /* - * No port was specified. + * No port was specified. * * If the url has the host specified in IP literal notation, the * host entry is terminated with a null character. The next string @@ -329,13 +329,13 @@ Ns_AbsoluteUrl(Ns_DString *dsPtr, const char *url, const char *base) proto = bproto; } assert(proto != NULL); - + if (host == NULL) { host = bhost; port = bport; } assert(host != NULL); - + if (path == NULL) { path = bpath; } @@ -348,7 +348,7 @@ Ns_AbsoluteUrl(Ns_DString *dsPtr, const char *url, const char *base) */ Ns_DStringVarAppend(dsPtr, proto, "://", host, (char *)0); } else { - Ns_DStringVarAppend(dsPtr, proto, "://[", host, "]", (char *)0); + Ns_DStringVarAppend(dsPtr, proto, "://[", host, "]", (char *)0); } if (port != NULL) { Ns_DStringVarAppend(dsPtr, ":", port, (char *)0); @@ -421,7 +421,7 @@ NsTclParseUrlObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj("tail", 4)); Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj(tail, -1)); } - + Tcl_SetObjResult(interp, resultObj); } else { @@ -430,7 +430,7 @@ NsTclParseUrlObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, } ns_free(url); } - + return result; } @@ -458,7 +458,7 @@ NsTclAbsoluteUrlObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int ob char *urlString, *baseString; Ns_ObjvSpec args[] = { {"partialurl", Ns_ObjvString, &urlString, NULL}, - {"baseurl", Ns_ObjvString, &baseString, NULL}, + {"baseurl", Ns_ObjvString, &baseString, NULL}, {NULL, NULL, NULL, NULL} }; @@ -476,7 +476,7 @@ NsTclAbsoluteUrlObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int ob result = TCL_ERROR; } } - + return result; } diff --git a/nsd/url2file.c b/nsd/url2file.c index 3bf912f6..ce60b204 100644 --- a/nsd/url2file.c +++ b/nsd/url2file.c @@ -107,12 +107,18 @@ static Ns_ReturnCode ConfigServerUrl2File(const char *server) { NsServer *servPtr; + Ns_ReturnCode result; servPtr = NsGetServer(server); - Ns_RegisterUrl2FileProc(server, "/", Ns_FastUrl2FileProc, NULL, servPtr, 0u); - Ns_SetUrlToFileProc(server, NsUrlToFileProc); + if (likely(servPtr != NULL)) { + Ns_RegisterUrl2FileProc(server, "/", Ns_FastUrl2FileProc, NULL, servPtr, 0u); + Ns_SetUrlToFileProc(server, NsUrlToFileProc); + result = NS_OK; + } else { + result = NS_ERROR; + } - return NS_OK; + return result; } @@ -141,18 +147,21 @@ Ns_RegisterUrl2FileProc(const char *server, const char *url, unsigned int flags) { NsServer *servPtr = NsGetServer(server); - Url2File *u2fPtr; - - servPtr->fastpath.url2file = NULL; - u2fPtr = ns_malloc(sizeof(Url2File)); - u2fPtr->proc = proc; - u2fPtr->deleteCallback = deleteCallback; - u2fPtr->arg = arg; - u2fPtr->flags = flags; - u2fPtr->refcnt = 1; - Ns_MutexLock(&ulock); - Ns_UrlSpecificSet(server, "x", url, uid, u2fPtr, flags, FreeUrl2File); - Ns_MutexUnlock(&ulock); + + if (servPtr != NULL) { + Url2File *u2fPtr; + + servPtr->fastpath.url2file = NULL; + u2fPtr = ns_malloc(sizeof(Url2File)); + u2fPtr->proc = proc; + u2fPtr->deleteCallback = deleteCallback; + u2fPtr->arg = arg; + u2fPtr->flags = flags; + u2fPtr->refcnt = 1; + Ns_MutexLock(&ulock); + Ns_UrlSpecificSet(server, "x", url, uid, u2fPtr, flags, FreeUrl2File); + Ns_MutexUnlock(&ulock); + } } @@ -231,14 +240,21 @@ Ns_FastUrl2FileProc(Ns_DString *dsPtr, const char *url, const void *arg) Ns_ReturnCode Ns_UrlToFile(Ns_DString *dsPtr, const char *server, const char *url) { - NsServer *servPtr; + NsServer *servPtr; + Ns_ReturnCode status; NS_NONNULL_ASSERT(dsPtr != NULL); NS_NONNULL_ASSERT(server != NULL); NS_NONNULL_ASSERT(url != NULL); servPtr = NsGetServer(server); - return NsUrlToFile(dsPtr, servPtr, url); + if (likely(servPtr != NULL)) { + status = NsUrlToFile(dsPtr, servPtr, url); + } else { + status = NS_ERROR; + } + + return status; } Ns_ReturnCode @@ -301,7 +317,9 @@ Ns_SetUrlToFileProc(const char *server, Ns_UrlToFileProc *procPtr) { NsServer *servPtr = NsGetServer(server); - servPtr->fastpath.url2file = procPtr; + if (servPtr != NULL) { + servPtr->fastpath.url2file = procPtr; + } } @@ -325,8 +343,14 @@ Ns_ReturnCode NsUrlToFileProc(Ns_DString *dsPtr, const char *server, const char *url) { const NsServer *servPtr = NsGetServer(server); + Ns_ReturnCode result; - return Ns_FastUrl2FileProc(dsPtr, url, servPtr); + if (likely(servPtr != NULL)) { + result = Ns_FastUrl2FileProc(dsPtr, url, servPtr); + } else { + result = NS_ERROR; + } + return result; } diff --git a/nsd/urlencode.c b/nsd/urlencode.c index 99f26cb6..56e3f9aa 100644 --- a/nsd/urlencode.c +++ b/nsd/urlencode.c @@ -11,7 +11,7 @@ * * The Original Code is AOLserver Code and related documentation * distributed by AOL. - * + * * The Initial Developer of the Original Code is America Online, * Inc. Portions created by AOL are Copyright (C) 1999 America Online, * Inc. All Rights Reserved. @@ -27,13 +27,13 @@ * version of this file under either the License or the GPL. */ -/* +/* * urlencode.c -- * * Encode and decode strings with percent encoding, as covered in * - RFC 3986 (Uniform Resource Identifier (URI): Generic Syntax) * - RFC 6265 (HTTP State Management Mechanism) - * + * * When the code is complied with RFC1738 activated, the encoding * of prior versions is used. */ @@ -46,7 +46,7 @@ */ typedef struct ByteKey { - int len; /* Length required to encode string. */ + int len; /* Length required to encode string. */ const char *str; /* String for multibyte encoded character. */ } ByteKey; @@ -85,69 +85,69 @@ static char *UrlDecode(Ns_DString *dsPtr, const char *urlSegment, */ static const ByteKey query_enc[] = { - {3, "00"}, {3, "01"}, {3, "02"}, {3, "03"}, - {3, "04"}, {3, "05"}, {3, "06"}, {3, "07"}, - {3, "08"}, {3, "09"}, {3, "0a"}, {3, "0b"}, - {3, "0c"}, {3, "0d"}, {3, "0e"}, {3, "0f"}, - {3, "10"}, {3, "11"}, {3, "12"}, {3, "13"}, - {3, "14"}, {3, "15"}, {3, "16"}, {3, "17"}, - {3, "18"}, {3, "19"}, {3, "1a"}, {3, "1b"}, - {3, "1c"}, {3, "1d"}, {3, "1e"}, {3, "1f"}, - {1, "20"}, {1, NULL}, {3, "22"}, {3, "23"}, - {3, "24"}, {3, "25"}, {3, "26"}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {3, "2b"}, - {3, "2c"}, {1, NULL}, {1, NULL}, {3, "2f"}, - {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {3, "3a"}, {3, "3b"}, - {3, "3c"}, {3, "3d"}, {3, "3e"}, {3, "3f"}, - {3, "40"}, {1, NULL}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {3, "5b"}, - {3, "5c"}, {3, "5d"}, {3, "5e"}, {1, NULL}, - {3, "60"}, {1, NULL}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {3, "7b"}, - {3, "7c"}, {3, "7d"}, {1, NULL}, {3, "7f"}, - {3, "80"}, {3, "81"}, {3, "82"}, {3, "83"}, - {3, "84"}, {3, "85"}, {3, "86"}, {3, "87"}, - {3, "88"}, {3, "89"}, {3, "8a"}, {3, "8b"}, - {3, "8c"}, {3, "8d"}, {3, "8e"}, {3, "8f"}, - {3, "90"}, {3, "91"}, {3, "92"}, {3, "93"}, - {3, "94"}, {3, "95"}, {3, "96"}, {3, "97"}, - {3, "98"}, {3, "99"}, {3, "9a"}, {3, "9b"}, - {3, "9c"}, {3, "9d"}, {3, "9e"}, {3, "9f"}, - {3, "a0"}, {3, "a1"}, {3, "a2"}, {3, "a3"}, - {3, "a4"}, {3, "a5"}, {3, "a6"}, {3, "a7"}, - {3, "a8"}, {3, "a9"}, {3, "aa"}, {3, "ab"}, - {3, "ac"}, {3, "ad"}, {3, "ae"}, {3, "af"}, - {3, "b0"}, {3, "b1"}, {3, "b2"}, {3, "b3"}, - {3, "b4"}, {3, "b5"}, {3, "b6"}, {3, "b7"}, - {3, "b8"}, {3, "b9"}, {3, "ba"}, {3, "bb"}, - {3, "bc"}, {3, "bd"}, {3, "be"}, {3, "bf"}, - {3, "c0"}, {3, "c1"}, {3, "c2"}, {3, "c3"}, - {3, "c4"}, {3, "c5"}, {3, "c6"}, {3, "c7"}, - {3, "c8"}, {3, "c9"}, {3, "ca"}, {3, "cb"}, - {3, "cc"}, {3, "cd"}, {3, "ce"}, {3, "cf"}, - {3, "d0"}, {3, "d1"}, {3, "d2"}, {3, "d3"}, - {3, "d4"}, {3, "d5"}, {3, "d6"}, {3, "d7"}, - {3, "d8"}, {3, "d9"}, {3, "da"}, {3, "db"}, - {3, "dc"}, {3, "dd"}, {3, "de"}, {3, "df"}, - {3, "e0"}, {3, "e1"}, {3, "e2"}, {3, "e3"}, - {3, "e4"}, {3, "e5"}, {3, "e6"}, {3, "e7"}, - {3, "e8"}, {3, "e9"}, {3, "ea"}, {3, "eb"}, - {3, "ec"}, {3, "ed"}, {3, "ee"}, {3, "ef"}, - {3, "f0"}, {3, "f1"}, {3, "f2"}, {3, "f3"}, - {3, "f4"}, {3, "f5"}, {3, "f6"}, {3, "f7"}, - {3, "f8"}, {3, "f9"}, {3, "fa"}, {3, "fb"}, + {3, "00"}, {3, "01"}, {3, "02"}, {3, "03"}, + {3, "04"}, {3, "05"}, {3, "06"}, {3, "07"}, + {3, "08"}, {3, "09"}, {3, "0a"}, {3, "0b"}, + {3, "0c"}, {3, "0d"}, {3, "0e"}, {3, "0f"}, + {3, "10"}, {3, "11"}, {3, "12"}, {3, "13"}, + {3, "14"}, {3, "15"}, {3, "16"}, {3, "17"}, + {3, "18"}, {3, "19"}, {3, "1a"}, {3, "1b"}, + {3, "1c"}, {3, "1d"}, {3, "1e"}, {3, "1f"}, + {1, "20"}, {1, NULL}, {3, "22"}, {3, "23"}, + {3, "24"}, {3, "25"}, {3, "26"}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {3, "2b"}, + {3, "2c"}, {1, NULL}, {1, NULL}, {3, "2f"}, + {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {3, "3a"}, {3, "3b"}, + {3, "3c"}, {3, "3d"}, {3, "3e"}, {3, "3f"}, + {3, "40"}, {1, NULL}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {3, "5b"}, + {3, "5c"}, {3, "5d"}, {3, "5e"}, {1, NULL}, + {3, "60"}, {1, NULL}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {3, "7b"}, + {3, "7c"}, {3, "7d"}, {1, NULL}, {3, "7f"}, + {3, "80"}, {3, "81"}, {3, "82"}, {3, "83"}, + {3, "84"}, {3, "85"}, {3, "86"}, {3, "87"}, + {3, "88"}, {3, "89"}, {3, "8a"}, {3, "8b"}, + {3, "8c"}, {3, "8d"}, {3, "8e"}, {3, "8f"}, + {3, "90"}, {3, "91"}, {3, "92"}, {3, "93"}, + {3, "94"}, {3, "95"}, {3, "96"}, {3, "97"}, + {3, "98"}, {3, "99"}, {3, "9a"}, {3, "9b"}, + {3, "9c"}, {3, "9d"}, {3, "9e"}, {3, "9f"}, + {3, "a0"}, {3, "a1"}, {3, "a2"}, {3, "a3"}, + {3, "a4"}, {3, "a5"}, {3, "a6"}, {3, "a7"}, + {3, "a8"}, {3, "a9"}, {3, "aa"}, {3, "ab"}, + {3, "ac"}, {3, "ad"}, {3, "ae"}, {3, "af"}, + {3, "b0"}, {3, "b1"}, {3, "b2"}, {3, "b3"}, + {3, "b4"}, {3, "b5"}, {3, "b6"}, {3, "b7"}, + {3, "b8"}, {3, "b9"}, {3, "ba"}, {3, "bb"}, + {3, "bc"}, {3, "bd"}, {3, "be"}, {3, "bf"}, + {3, "c0"}, {3, "c1"}, {3, "c2"}, {3, "c3"}, + {3, "c4"}, {3, "c5"}, {3, "c6"}, {3, "c7"}, + {3, "c8"}, {3, "c9"}, {3, "ca"}, {3, "cb"}, + {3, "cc"}, {3, "cd"}, {3, "ce"}, {3, "cf"}, + {3, "d0"}, {3, "d1"}, {3, "d2"}, {3, "d3"}, + {3, "d4"}, {3, "d5"}, {3, "d6"}, {3, "d7"}, + {3, "d8"}, {3, "d9"}, {3, "da"}, {3, "db"}, + {3, "dc"}, {3, "dd"}, {3, "de"}, {3, "df"}, + {3, "e0"}, {3, "e1"}, {3, "e2"}, {3, "e3"}, + {3, "e4"}, {3, "e5"}, {3, "e6"}, {3, "e7"}, + {3, "e8"}, {3, "e9"}, {3, "ea"}, {3, "eb"}, + {3, "ec"}, {3, "ed"}, {3, "ee"}, {3, "ef"}, + {3, "f0"}, {3, "f1"}, {3, "f2"}, {3, "f3"}, + {3, "f4"}, {3, "f5"}, {3, "f6"}, {3, "f7"}, + {3, "f8"}, {3, "f9"}, {3, "fa"}, {3, "fb"}, {3, "fc"}, {3, "fd"}, {3, "fe"}, {3, "ff"} }; @@ -164,69 +164,69 @@ static const ByteKey query_enc[] = { */ static const ByteKey path_enc[] = { - {3, "00"}, {3, "01"}, {3, "02"}, {3, "03"}, - {3, "04"}, {3, "05"}, {3, "06"}, {3, "07"}, - {3, "08"}, {3, "09"}, {3, "0a"}, {3, "0b"}, - {3, "0c"}, {3, "0d"}, {3, "0e"}, {3, "0f"}, - {3, "10"}, {3, "11"}, {3, "12"}, {3, "13"}, - {3, "14"}, {3, "15"}, {3, "16"}, {3, "17"}, - {3, "18"}, {3, "19"}, {3, "1a"}, {3, "1b"}, - {3, "1c"}, {3, "1d"}, {3, "1e"}, {3, "1f"}, - {3, "20"}, {1, NULL}, {3, "22"}, {3, "23"}, - {1, NULL}, {3, "25"}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {3, "2f"}, - {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {3, "3b"}, - {3, "3c"}, {3, "3d"}, {3, "3e"}, {3, "3f"}, - {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {3, "5b"}, - {3, "5c"}, {3, "5d"}, {3, "5e"}, {1, NULL}, - {3, "60"}, {1, NULL}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - {1, NULL}, {1, NULL}, {1, NULL}, {3, "7b"}, - {3, "7c"}, {3, "7d"}, {1, NULL}, {3, "7f"}, - {3, "80"}, {3, "81"}, {3, "82"}, {3, "83"}, - {3, "84"}, {3, "85"}, {3, "86"}, {3, "87"}, - {3, "88"}, {3, "89"}, {3, "8a"}, {3, "8b"}, - {3, "8c"}, {3, "8d"}, {3, "8e"}, {3, "8f"}, - {3, "90"}, {3, "91"}, {3, "92"}, {3, "93"}, - {3, "94"}, {3, "95"}, {3, "96"}, {3, "97"}, - {3, "98"}, {3, "99"}, {3, "9a"}, {3, "9b"}, - {3, "9c"}, {3, "9d"}, {3, "9e"}, {3, "9f"}, - {3, "a0"}, {3, "a1"}, {3, "a2"}, {3, "a3"}, - {3, "a4"}, {3, "a5"}, {3, "a6"}, {3, "a7"}, - {3, "a8"}, {3, "a9"}, {3, "aa"}, {3, "ab"}, - {3, "ac"}, {3, "ad"}, {3, "ae"}, {3, "af"}, - {3, "b0"}, {3, "b1"}, {3, "b2"}, {3, "b3"}, - {3, "b4"}, {3, "b5"}, {3, "b6"}, {3, "b7"}, - {3, "b8"}, {3, "b9"}, {3, "ba"}, {3, "bb"}, - {3, "bc"}, {3, "bd"}, {3, "be"}, {3, "bf"}, - {3, "c0"}, {3, "c1"}, {3, "c2"}, {3, "c3"}, - {3, "c4"}, {3, "c5"}, {3, "c6"}, {3, "c7"}, - {3, "c8"}, {3, "c9"}, {3, "ca"}, {3, "cb"}, - {3, "cc"}, {3, "cd"}, {3, "ce"}, {3, "cf"}, - {3, "d0"}, {3, "d1"}, {3, "d2"}, {3, "d3"}, - {3, "d4"}, {3, "d5"}, {3, "d6"}, {3, "d7"}, - {3, "d8"}, {3, "d9"}, {3, "da"}, {3, "db"}, - {3, "dc"}, {3, "dd"}, {3, "de"}, {3, "df"}, - {3, "e0"}, {3, "e1"}, {3, "e2"}, {3, "e3"}, - {3, "e4"}, {3, "e5"}, {3, "e6"}, {3, "e7"}, - {3, "e8"}, {3, "e9"}, {3, "ea"}, {3, "eb"}, - {3, "ec"}, {3, "ed"}, {3, "ee"}, {3, "ef"}, - {3, "f0"}, {3, "f1"}, {3, "f2"}, {3, "f3"}, - {3, "f4"}, {3, "f5"}, {3, "f6"}, {3, "f7"}, - {3, "f8"}, {3, "f9"}, {3, "fa"}, {3, "fb"}, + {3, "00"}, {3, "01"}, {3, "02"}, {3, "03"}, + {3, "04"}, {3, "05"}, {3, "06"}, {3, "07"}, + {3, "08"}, {3, "09"}, {3, "0a"}, {3, "0b"}, + {3, "0c"}, {3, "0d"}, {3, "0e"}, {3, "0f"}, + {3, "10"}, {3, "11"}, {3, "12"}, {3, "13"}, + {3, "14"}, {3, "15"}, {3, "16"}, {3, "17"}, + {3, "18"}, {3, "19"}, {3, "1a"}, {3, "1b"}, + {3, "1c"}, {3, "1d"}, {3, "1e"}, {3, "1f"}, + {3, "20"}, {1, NULL}, {3, "22"}, {3, "23"}, + {1, NULL}, {3, "25"}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {3, "2f"}, + {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {3, "3b"}, + {3, "3c"}, {3, "3d"}, {3, "3e"}, {3, "3f"}, + {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {3, "5b"}, + {3, "5c"}, {3, "5d"}, {3, "5e"}, {1, NULL}, + {3, "60"}, {1, NULL}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + {1, NULL}, {1, NULL}, {1, NULL}, {3, "7b"}, + {3, "7c"}, {3, "7d"}, {1, NULL}, {3, "7f"}, + {3, "80"}, {3, "81"}, {3, "82"}, {3, "83"}, + {3, "84"}, {3, "85"}, {3, "86"}, {3, "87"}, + {3, "88"}, {3, "89"}, {3, "8a"}, {3, "8b"}, + {3, "8c"}, {3, "8d"}, {3, "8e"}, {3, "8f"}, + {3, "90"}, {3, "91"}, {3, "92"}, {3, "93"}, + {3, "94"}, {3, "95"}, {3, "96"}, {3, "97"}, + {3, "98"}, {3, "99"}, {3, "9a"}, {3, "9b"}, + {3, "9c"}, {3, "9d"}, {3, "9e"}, {3, "9f"}, + {3, "a0"}, {3, "a1"}, {3, "a2"}, {3, "a3"}, + {3, "a4"}, {3, "a5"}, {3, "a6"}, {3, "a7"}, + {3, "a8"}, {3, "a9"}, {3, "aa"}, {3, "ab"}, + {3, "ac"}, {3, "ad"}, {3, "ae"}, {3, "af"}, + {3, "b0"}, {3, "b1"}, {3, "b2"}, {3, "b3"}, + {3, "b4"}, {3, "b5"}, {3, "b6"}, {3, "b7"}, + {3, "b8"}, {3, "b9"}, {3, "ba"}, {3, "bb"}, + {3, "bc"}, {3, "bd"}, {3, "be"}, {3, "bf"}, + {3, "c0"}, {3, "c1"}, {3, "c2"}, {3, "c3"}, + {3, "c4"}, {3, "c5"}, {3, "c6"}, {3, "c7"}, + {3, "c8"}, {3, "c9"}, {3, "ca"}, {3, "cb"}, + {3, "cc"}, {3, "cd"}, {3, "ce"}, {3, "cf"}, + {3, "d0"}, {3, "d1"}, {3, "d2"}, {3, "d3"}, + {3, "d4"}, {3, "d5"}, {3, "d6"}, {3, "d7"}, + {3, "d8"}, {3, "d9"}, {3, "da"}, {3, "db"}, + {3, "dc"}, {3, "dd"}, {3, "de"}, {3, "df"}, + {3, "e0"}, {3, "e1"}, {3, "e2"}, {3, "e3"}, + {3, "e4"}, {3, "e5"}, {3, "e6"}, {3, "e7"}, + {3, "e8"}, {3, "e9"}, {3, "ea"}, {3, "eb"}, + {3, "ec"}, {3, "ed"}, {3, "ee"}, {3, "ef"}, + {3, "f0"}, {3, "f1"}, {3, "f2"}, {3, "f3"}, + {3, "f4"}, {3, "f5"}, {3, "f6"}, {3, "f7"}, + {3, "f8"}, {3, "f9"}, {3, "fa"}, {3, "fb"}, {3, "fc"}, {3, "fd"}, {3, "fe"}, {3, "ff"} }; #else @@ -243,7 +243,7 @@ static const ByteKey path_enc[] = { * characters above 7f are encoded, 'unreserved' characters are never * encoded. * - * The query part of a URL is defined as: + * The query part of a URL is defined as: * * query = *( pchar / "/" / "?" ) * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" @@ -256,7 +256,7 @@ static const ByteKey path_enc[] = { * meanings (https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1) * so only the following sub-delims are allowed literally. * - * query-sub-delims1 = "!" / "$" / "'" / "(" / ")" / "*" / "," / ";" + * query-sub-delims1 = "!" / "$" / "'" / "(" / ")" / "*" / "," / ";" * * In order to make query-component usable for encoding/decoding cookies, * the characters "," and ";" have to be percent-encoded as well. @@ -272,75 +272,75 @@ static const ByteKey path_enc[] = { * * Unprotected characters: * - * ! $ ' ( ) * + - . / 0 1 2 3 4 5 6 7 8 9 : ? @ - * A B C D E F G H I J K L M N O P Q R S T U V W X Y Z _ + * ! $ ' ( ) * + - . / 0 1 2 3 4 5 6 7 8 9 : ? @ + * A B C D E F G H I J K L M N O P Q R S T U V W X Y Z _ * a b c d e f g h i j k l m n o p q r s t u v w x y z ~ */ static const ByteKey query_enc[] = { - /* 0x00 */ {3, "00"}, {3, "01"}, {3, "02"}, {3, "03"}, - /* 0x04 */ {3, "04"}, {3, "05"}, {3, "06"}, {3, "07"}, - /* 0x08 */ {3, "08"}, {3, "09"}, {3, "0a"}, {3, "0b"}, - /* 0x0c */ {3, "0c"}, {3, "0d"}, {3, "0e"}, {3, "0f"}, - /* 0x10 */ {3, "10"}, {3, "11"}, {3, "12"}, {3, "13"}, - /* 0x14 */ {3, "14"}, {3, "15"}, {3, "16"}, {3, "17"}, - /* 0x18 */ {3, "18"}, {3, "19"}, {3, "1a"}, {3, "1b"}, - /* 0x1c */ {3, "1c"}, {3, "1d"}, {3, "1e"}, {3, "1f"}, - /* 0x20 */ {1, NULL}, {1, NULL}, {3, "22"}, {3, "23"}, - /* 0x24 */ {1, NULL}, {3, "25"}, {3, "26"}, {1, NULL}, - /* 0x28 */ {1, NULL}, {1, NULL}, {1, NULL}, {3, "2b"}, - /* 0x2c */ {3, "2c"}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x30 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x34 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x38 */ {1, NULL}, {1, NULL}, {1, NULL}, {3, "3b"}, - /* 0x3c */ {3, "3c"}, {3, "3d"}, {3, "3e"}, {1, NULL}, - /* 0x40 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x44 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x48 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x4c */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x50 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x54 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x58 */ {1, NULL}, {1, NULL}, {1, NULL}, {3, "5b"}, - /* 0x5c */ {3, "5c"}, {3, "5d"}, {3, "5e"}, {1, NULL}, - /* 0x60 */ {3, "60"}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x64 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x68 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x6c */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x70 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x74 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x78 */ {1, NULL}, {1, NULL}, {1, NULL}, {3, "7b"}, - /* 0x7c */ {3, "7c"}, {3, "7d"}, {1, NULL}, {3, "7f"}, - /* 0x80 */ {3, "80"}, {3, "81"}, {3, "82"}, {3, "83"}, - /* 0x84 */ {3, "84"}, {3, "85"}, {3, "86"}, {3, "87"}, - /* 0x88 */ {3, "88"}, {3, "89"}, {3, "8a"}, {3, "8b"}, - /* 0x8c */ {3, "8c"}, {3, "8d"}, {3, "8e"}, {3, "8f"}, - /* 0x90 */ {3, "90"}, {3, "91"}, {3, "92"}, {3, "93"}, - /* 0x94 */ {3, "94"}, {3, "95"}, {3, "96"}, {3, "97"}, - /* 0x98 */ {3, "98"}, {3, "99"}, {3, "9a"}, {3, "9b"}, - /* 0x9c */ {3, "9c"}, {3, "9d"}, {3, "9e"}, {3, "9f"}, - /* 0xa0 */ {3, "a0"}, {3, "a1"}, {3, "a2"}, {3, "a3"}, - /* 0xa4 */ {3, "a4"}, {3, "a5"}, {3, "a6"}, {3, "a7"}, - /* 0xa8 */ {3, "a8"}, {3, "a9"}, {3, "aa"}, {3, "ab"}, - /* 0xac */ {3, "ac"}, {3, "ad"}, {3, "ae"}, {3, "af"}, - /* 0xb0 */ {3, "b0"}, {3, "b1"}, {3, "b2"}, {3, "b3"}, - /* 0xb4 */ {3, "b4"}, {3, "b5"}, {3, "b6"}, {3, "b7"}, - /* 0xb8 */ {3, "b8"}, {3, "b9"}, {3, "ba"}, {3, "bb"}, - /* 0xbc */ {3, "bc"}, {3, "bd"}, {3, "be"}, {3, "bf"}, - /* 0xc0 */ {3, "c0"}, {3, "c1"}, {3, "c2"}, {3, "c3"}, - /* 0xc4 */ {3, "c4"}, {3, "c5"}, {3, "c6"}, {3, "c7"}, - /* 0xc8 */ {3, "c8"}, {3, "c9"}, {3, "ca"}, {3, "cb"}, - /* 0xcc */ {3, "cc"}, {3, "cd"}, {3, "ce"}, {3, "cf"}, - /* 0xd0 */ {3, "d0"}, {3, "d1"}, {3, "d2"}, {3, "d3"}, - /* 0xd4 */ {3, "d4"}, {3, "d5"}, {3, "d6"}, {3, "d7"}, - /* 0xd8 */ {3, "d8"}, {3, "d9"}, {3, "da"}, {3, "db"}, - /* 0xdc */ {3, "dc"}, {3, "dd"}, {3, "de"}, {3, "df"}, - /* 0xe0 */ {3, "e0"}, {3, "e1"}, {3, "e2"}, {3, "e3"}, - /* 0xe4 */ {3, "e4"}, {3, "e5"}, {3, "e6"}, {3, "e7"}, - /* 0xe8 */ {3, "e8"}, {3, "e9"}, {3, "ea"}, {3, "eb"}, - /* 0xec */ {3, "ec"}, {3, "ed"}, {3, "ee"}, {3, "ef"}, - /* 0xf0 */ {3, "f0"}, {3, "f1"}, {3, "f2"}, {3, "f3"}, - /* 0xf4 */ {3, "f4"}, {3, "f5"}, {3, "f6"}, {3, "f7"}, - /* 0xf8 */ {3, "f8"}, {3, "f9"}, {3, "fa"}, {3, "fb"}, + /* 0x00 */ {3, "00"}, {3, "01"}, {3, "02"}, {3, "03"}, + /* 0x04 */ {3, "04"}, {3, "05"}, {3, "06"}, {3, "07"}, + /* 0x08 */ {3, "08"}, {3, "09"}, {3, "0a"}, {3, "0b"}, + /* 0x0c */ {3, "0c"}, {3, "0d"}, {3, "0e"}, {3, "0f"}, + /* 0x10 */ {3, "10"}, {3, "11"}, {3, "12"}, {3, "13"}, + /* 0x14 */ {3, "14"}, {3, "15"}, {3, "16"}, {3, "17"}, + /* 0x18 */ {3, "18"}, {3, "19"}, {3, "1a"}, {3, "1b"}, + /* 0x1c */ {3, "1c"}, {3, "1d"}, {3, "1e"}, {3, "1f"}, + /* 0x20 */ {1, NULL}, {1, NULL}, {3, "22"}, {3, "23"}, + /* 0x24 */ {1, NULL}, {3, "25"}, {3, "26"}, {1, NULL}, + /* 0x28 */ {1, NULL}, {1, NULL}, {1, NULL}, {3, "2b"}, + /* 0x2c */ {3, "2c"}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x30 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x34 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x38 */ {1, NULL}, {1, NULL}, {1, NULL}, {3, "3b"}, + /* 0x3c */ {3, "3c"}, {3, "3d"}, {3, "3e"}, {1, NULL}, + /* 0x40 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x44 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x48 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x4c */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x50 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x54 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x58 */ {1, NULL}, {1, NULL}, {1, NULL}, {3, "5b"}, + /* 0x5c */ {3, "5c"}, {3, "5d"}, {3, "5e"}, {1, NULL}, + /* 0x60 */ {3, "60"}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x64 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x68 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x6c */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x70 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x74 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x78 */ {1, NULL}, {1, NULL}, {1, NULL}, {3, "7b"}, + /* 0x7c */ {3, "7c"}, {3, "7d"}, {1, NULL}, {3, "7f"}, + /* 0x80 */ {3, "80"}, {3, "81"}, {3, "82"}, {3, "83"}, + /* 0x84 */ {3, "84"}, {3, "85"}, {3, "86"}, {3, "87"}, + /* 0x88 */ {3, "88"}, {3, "89"}, {3, "8a"}, {3, "8b"}, + /* 0x8c */ {3, "8c"}, {3, "8d"}, {3, "8e"}, {3, "8f"}, + /* 0x90 */ {3, "90"}, {3, "91"}, {3, "92"}, {3, "93"}, + /* 0x94 */ {3, "94"}, {3, "95"}, {3, "96"}, {3, "97"}, + /* 0x98 */ {3, "98"}, {3, "99"}, {3, "9a"}, {3, "9b"}, + /* 0x9c */ {3, "9c"}, {3, "9d"}, {3, "9e"}, {3, "9f"}, + /* 0xa0 */ {3, "a0"}, {3, "a1"}, {3, "a2"}, {3, "a3"}, + /* 0xa4 */ {3, "a4"}, {3, "a5"}, {3, "a6"}, {3, "a7"}, + /* 0xa8 */ {3, "a8"}, {3, "a9"}, {3, "aa"}, {3, "ab"}, + /* 0xac */ {3, "ac"}, {3, "ad"}, {3, "ae"}, {3, "af"}, + /* 0xb0 */ {3, "b0"}, {3, "b1"}, {3, "b2"}, {3, "b3"}, + /* 0xb4 */ {3, "b4"}, {3, "b5"}, {3, "b6"}, {3, "b7"}, + /* 0xb8 */ {3, "b8"}, {3, "b9"}, {3, "ba"}, {3, "bb"}, + /* 0xbc */ {3, "bc"}, {3, "bd"}, {3, "be"}, {3, "bf"}, + /* 0xc0 */ {3, "c0"}, {3, "c1"}, {3, "c2"}, {3, "c3"}, + /* 0xc4 */ {3, "c4"}, {3, "c5"}, {3, "c6"}, {3, "c7"}, + /* 0xc8 */ {3, "c8"}, {3, "c9"}, {3, "ca"}, {3, "cb"}, + /* 0xcc */ {3, "cc"}, {3, "cd"}, {3, "ce"}, {3, "cf"}, + /* 0xd0 */ {3, "d0"}, {3, "d1"}, {3, "d2"}, {3, "d3"}, + /* 0xd4 */ {3, "d4"}, {3, "d5"}, {3, "d6"}, {3, "d7"}, + /* 0xd8 */ {3, "d8"}, {3, "d9"}, {3, "da"}, {3, "db"}, + /* 0xdc */ {3, "dc"}, {3, "dd"}, {3, "de"}, {3, "df"}, + /* 0xe0 */ {3, "e0"}, {3, "e1"}, {3, "e2"}, {3, "e3"}, + /* 0xe4 */ {3, "e4"}, {3, "e5"}, {3, "e6"}, {3, "e7"}, + /* 0xe8 */ {3, "e8"}, {3, "e9"}, {3, "ea"}, {3, "eb"}, + /* 0xec */ {3, "ec"}, {3, "ed"}, {3, "ee"}, {3, "ef"}, + /* 0xf0 */ {3, "f0"}, {3, "f1"}, {3, "f2"}, {3, "f3"}, + /* 0xf4 */ {3, "f4"}, {3, "f5"}, {3, "f6"}, {3, "f7"}, + /* 0xf8 */ {3, "f8"}, {3, "f9"}, {3, "fa"}, {3, "fb"}, /* 0xfc */ {3, "fc"}, {3, "fd"}, {3, "fe"}, {3, "ff"} }; @@ -350,20 +350,20 @@ static const ByteKey query_enc[] = { * a URI path component based on RFC 3986 (Uniform Resource Identifier * (URI): Generic Syntax, 2005) * - * The query part of a URL is defined as: + * The query part of a URL is defined as: * * segment = *pchar * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" * / "*" / "+" / "," / ";" / "=" - * + * * The RFC states that semicolon (";") and equals ("=") reserved * characters are often used to delimit parameters and parameter values * applicable to that segment (whatever "often" means!). To be on the safe * side, and to support that characters as part of the segment, these are * encoded. - * + * * segment-sub-delims = "!" / "$" / "&" / "'" / "(" / ")" * / "*" / "+" / "," @@ -375,76 +375,76 @@ static const ByteKey query_enc[] = { * * Unprotected characters: * - * ! $ & ' ( ) * + , - . 0 1 2 3 4 5 6 7 8 9 : @ - * A B C D E F G H I J K L M N O P Q R S T U V W X Y Z _ + * ! $ & ' ( ) * + , - . 0 1 2 3 4 5 6 7 8 9 : @ + * A B C D E F G H I J K L M N O P Q R S T U V W X Y Z _ * a b c d e f g h i j k l m n o p q r s t u v w x y z ~ */ static const ByteKey path_enc[] = { - /* 0x00 */ {3, "00"}, {3, "01"}, {3, "02"}, {3, "03"}, - /* 0x04 */ {3, "04"}, {3, "05"}, {3, "06"}, {3, "07"}, - /* 0x08 */ {3, "08"}, {3, "09"}, {3, "0a"}, {3, "0b"}, - /* 0x0c */ {3, "0c"}, {3, "0d"}, {3, "0e"}, {3, "0f"}, - /* 0x10 */ {3, "10"}, {3, "11"}, {3, "12"}, {3, "13"}, - /* 0x14 */ {3, "14"}, {3, "15"}, {3, "16"}, {3, "17"}, - /* 0x18 */ {3, "18"}, {3, "19"}, {3, "1a"}, {3, "1b"}, - /* 0x1c */ {3, "1c"}, {3, "1d"}, {3, "1e"}, {3, "1f"}, - /* 0x20 */ {3, "20"}, {1, NULL}, {3, "22"}, {3, "23"}, - /* 0x24 */ {1, NULL}, {3, "25"}, {1, NULL}, {1, NULL}, - /* 0x28 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x2c */ {1, NULL}, {1, NULL}, {1, NULL}, {3, "2f"}, - /* 0x30 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x34 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x38 */ {1, NULL}, {1, NULL}, {1, NULL}, {3, "3b"}, - /* 0x3c */ {3, "3c"}, {3, "3d"}, {3, "3e"}, {3, "3f"}, - /* 0x40 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x44 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x48 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x4c */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x50 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x54 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x58 */ {1, NULL}, {1, NULL}, {1, NULL}, {3, "5b"}, - /* 0x5c */ {3, "5c"}, {3, "5d"}, {3, "5e"}, {1, NULL}, - /* 0x60 */ {3, "60"}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x64 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x68 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x6c */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x70 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x74 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x78 */ {1, NULL}, {1, NULL}, {1, NULL}, {3, "7b"}, - /* 0x7c */ {3, "7c"}, {3, "7d"}, {1, NULL}, {3, "7f"}, - /* 0x80 */ {3, "80"}, {3, "81"}, {3, "82"}, {3, "83"}, - /* 0x84 */ {3, "84"}, {3, "85"}, {3, "86"}, {3, "87"}, - /* 0x88 */ {3, "88"}, {3, "89"}, {3, "8a"}, {3, "8b"}, - /* 0x8c */ {3, "8c"}, {3, "8d"}, {3, "8e"}, {3, "8f"}, - /* 0x90 */ {3, "90"}, {3, "91"}, {3, "92"}, {3, "93"}, - /* 0x94 */ {3, "94"}, {3, "95"}, {3, "96"}, {3, "97"}, - /* 0x98 */ {3, "98"}, {3, "99"}, {3, "9a"}, {3, "9b"}, - /* 0x9c */ {3, "9c"}, {3, "9d"}, {3, "9e"}, {3, "9f"}, - /* 0xa0 */ {3, "a0"}, {3, "a1"}, {3, "a2"}, {3, "a3"}, - /* 0xa4 */ {3, "a4"}, {3, "a5"}, {3, "a6"}, {3, "a7"}, - /* 0xa8 */ {3, "a8"}, {3, "a9"}, {3, "aa"}, {3, "ab"}, - /* 0xac */ {3, "ac"}, {3, "ad"}, {3, "ae"}, {3, "af"}, - /* 0xb0 */ {3, "b0"}, {3, "b1"}, {3, "b2"}, {3, "b3"}, - /* 0xb4 */ {3, "b4"}, {3, "b5"}, {3, "b6"}, {3, "b7"}, - /* 0xb8 */ {3, "b8"}, {3, "b9"}, {3, "ba"}, {3, "bb"}, - /* 0xbc */ {3, "bc"}, {3, "bd"}, {3, "be"}, {3, "bf"}, - /* 0xc0 */ {3, "c0"}, {3, "c1"}, {3, "c2"}, {3, "c3"}, - /* 0xc4 */ {3, "c4"}, {3, "c5"}, {3, "c6"}, {3, "c7"}, - /* 0xc8 */ {3, "c8"}, {3, "c9"}, {3, "ca"}, {3, "cb"}, - /* 0xcc */ {3, "cc"}, {3, "cd"}, {3, "ce"}, {3, "cf"}, - /* 0xd0 */ {3, "d0"}, {3, "d1"}, {3, "d2"}, {3, "d3"}, - /* 0xd4 */ {3, "d4"}, {3, "d5"}, {3, "d6"}, {3, "d7"}, - /* 0xd8 */ {3, "d8"}, {3, "d9"}, {3, "da"}, {3, "db"}, - /* 0xdc */ {3, "dc"}, {3, "dd"}, {3, "de"}, {3, "df"}, - /* 0xe0 */ {3, "e0"}, {3, "e1"}, {3, "e2"}, {3, "e3"}, - /* 0xe4 */ {3, "e4"}, {3, "e5"}, {3, "e6"}, {3, "e7"}, - /* 0xe8 */ {3, "e8"}, {3, "e9"}, {3, "ea"}, {3, "eb"}, - /* 0xec */ {3, "ec"}, {3, "ed"}, {3, "ee"}, {3, "ef"}, - /* 0xf0 */ {3, "f0"}, {3, "f1"}, {3, "f2"}, {3, "f3"}, - /* 0xf4 */ {3, "f4"}, {3, "f5"}, {3, "f6"}, {3, "f7"}, - /* 0xf8 */ {3, "f8"}, {3, "f9"}, {3, "fa"}, {3, "fb"}, + /* 0x00 */ {3, "00"}, {3, "01"}, {3, "02"}, {3, "03"}, + /* 0x04 */ {3, "04"}, {3, "05"}, {3, "06"}, {3, "07"}, + /* 0x08 */ {3, "08"}, {3, "09"}, {3, "0a"}, {3, "0b"}, + /* 0x0c */ {3, "0c"}, {3, "0d"}, {3, "0e"}, {3, "0f"}, + /* 0x10 */ {3, "10"}, {3, "11"}, {3, "12"}, {3, "13"}, + /* 0x14 */ {3, "14"}, {3, "15"}, {3, "16"}, {3, "17"}, + /* 0x18 */ {3, "18"}, {3, "19"}, {3, "1a"}, {3, "1b"}, + /* 0x1c */ {3, "1c"}, {3, "1d"}, {3, "1e"}, {3, "1f"}, + /* 0x20 */ {3, "20"}, {1, NULL}, {3, "22"}, {3, "23"}, + /* 0x24 */ {1, NULL}, {3, "25"}, {1, NULL}, {1, NULL}, + /* 0x28 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x2c */ {1, NULL}, {1, NULL}, {1, NULL}, {3, "2f"}, + /* 0x30 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x34 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x38 */ {1, NULL}, {1, NULL}, {1, NULL}, {3, "3b"}, + /* 0x3c */ {3, "3c"}, {3, "3d"}, {3, "3e"}, {3, "3f"}, + /* 0x40 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x44 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x48 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x4c */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x50 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x54 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x58 */ {1, NULL}, {1, NULL}, {1, NULL}, {3, "5b"}, + /* 0x5c */ {3, "5c"}, {3, "5d"}, {3, "5e"}, {1, NULL}, + /* 0x60 */ {3, "60"}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x64 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x68 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x6c */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x70 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x74 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x78 */ {1, NULL}, {1, NULL}, {1, NULL}, {3, "7b"}, + /* 0x7c */ {3, "7c"}, {3, "7d"}, {1, NULL}, {3, "7f"}, + /* 0x80 */ {3, "80"}, {3, "81"}, {3, "82"}, {3, "83"}, + /* 0x84 */ {3, "84"}, {3, "85"}, {3, "86"}, {3, "87"}, + /* 0x88 */ {3, "88"}, {3, "89"}, {3, "8a"}, {3, "8b"}, + /* 0x8c */ {3, "8c"}, {3, "8d"}, {3, "8e"}, {3, "8f"}, + /* 0x90 */ {3, "90"}, {3, "91"}, {3, "92"}, {3, "93"}, + /* 0x94 */ {3, "94"}, {3, "95"}, {3, "96"}, {3, "97"}, + /* 0x98 */ {3, "98"}, {3, "99"}, {3, "9a"}, {3, "9b"}, + /* 0x9c */ {3, "9c"}, {3, "9d"}, {3, "9e"}, {3, "9f"}, + /* 0xa0 */ {3, "a0"}, {3, "a1"}, {3, "a2"}, {3, "a3"}, + /* 0xa4 */ {3, "a4"}, {3, "a5"}, {3, "a6"}, {3, "a7"}, + /* 0xa8 */ {3, "a8"}, {3, "a9"}, {3, "aa"}, {3, "ab"}, + /* 0xac */ {3, "ac"}, {3, "ad"}, {3, "ae"}, {3, "af"}, + /* 0xb0 */ {3, "b0"}, {3, "b1"}, {3, "b2"}, {3, "b3"}, + /* 0xb4 */ {3, "b4"}, {3, "b5"}, {3, "b6"}, {3, "b7"}, + /* 0xb8 */ {3, "b8"}, {3, "b9"}, {3, "ba"}, {3, "bb"}, + /* 0xbc */ {3, "bc"}, {3, "bd"}, {3, "be"}, {3, "bf"}, + /* 0xc0 */ {3, "c0"}, {3, "c1"}, {3, "c2"}, {3, "c3"}, + /* 0xc4 */ {3, "c4"}, {3, "c5"}, {3, "c6"}, {3, "c7"}, + /* 0xc8 */ {3, "c8"}, {3, "c9"}, {3, "ca"}, {3, "cb"}, + /* 0xcc */ {3, "cc"}, {3, "cd"}, {3, "ce"}, {3, "cf"}, + /* 0xd0 */ {3, "d0"}, {3, "d1"}, {3, "d2"}, {3, "d3"}, + /* 0xd4 */ {3, "d4"}, {3, "d5"}, {3, "d6"}, {3, "d7"}, + /* 0xd8 */ {3, "d8"}, {3, "d9"}, {3, "da"}, {3, "db"}, + /* 0xdc */ {3, "dc"}, {3, "dd"}, {3, "de"}, {3, "df"}, + /* 0xe0 */ {3, "e0"}, {3, "e1"}, {3, "e2"}, {3, "e3"}, + /* 0xe4 */ {3, "e4"}, {3, "e5"}, {3, "e6"}, {3, "e7"}, + /* 0xe8 */ {3, "e8"}, {3, "e9"}, {3, "ea"}, {3, "eb"}, + /* 0xec */ {3, "ec"}, {3, "ed"}, {3, "ee"}, {3, "ef"}, + /* 0xf0 */ {3, "f0"}, {3, "f1"}, {3, "f2"}, {3, "f3"}, + /* 0xf4 */ {3, "f4"}, {3, "f5"}, {3, "f6"}, {3, "f7"}, + /* 0xf8 */ {3, "f8"}, {3, "f9"}, {3, "fa"}, {3, "fb"}, /* 0xfc */ {3, "fc"}, {3, "fd"}, {3, "fe"}, {3, "ff"} }; #endif @@ -460,7 +460,7 @@ static const ByteKey path_enc[] = { * * cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E * ; US-ASCII characters excluding CTLs, - * ; whitespace DQUOTE, comma, semicolon, + * ; whitespace, DQUOTE, comma, semicolon, * ; and backslash * * In additions, '%' has to be encoded, because elsewise a raw string @@ -469,76 +469,76 @@ static const ByteKey path_enc[] = { * * This definition implies that a total of 89 characters are allowed * unencoded in a cookie: - * - * ! # $ & ' ( ) * + - . / 0 1 2 3 4 5 6 7 8 9 : < = > ? @ - * A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ ] ^ _ ` + * + * ! # $ & ' ( ) * + - . / 0 1 2 3 4 5 6 7 8 9 : < = > ? @ + * A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ ] ^ _ ` * a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~ */ static const ByteKey cookie_enc[] = { - /* 0x00 */ {3, "00"}, {3, "01"}, {3, "02"}, {3, "03"}, - /* 0x04 */ {3, "04"}, {3, "05"}, {3, "06"}, {3, "07"}, - /* 0x08 */ {3, "08"}, {3, "09"}, {3, "0a"}, {3, "0b"}, - /* 0x0c */ {3, "0c"}, {3, "0d"}, {3, "0e"}, {3, "0f"}, - /* 0x10 */ {3, "10"}, {3, "11"}, {3, "12"}, {3, "13"}, - /* 0x14 */ {3, "14"}, {3, "15"}, {3, "16"}, {3, "17"}, - /* 0x18 */ {3, "18"}, {3, "19"}, {3, "1a"}, {3, "1b"}, - /* 0x1c */ {3, "1c"}, {3, "1d"}, {3, "1e"}, {3, "1f"}, - /* 0x20 */ {3, "20"}, {1, NULL}, {3, "22"}, {1, NULL}, - /* 0x24 */ {1, NULL}, {3, "25"}, {1, NULL}, {1, NULL}, - /* 0x28 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x2c */ {3, "2c"}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x30 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x34 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x38 */ {1, NULL}, {1, NULL}, {1, NULL}, {3, "3b"}, - /* 0x3c */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x40 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x44 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x48 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x4c */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x50 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x54 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x58 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x5c */ {3, "5c"}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x60 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x64 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x68 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x6c */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x70 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x74 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x78 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, - /* 0x7c */ {1, NULL}, {1, NULL}, {1, NULL}, {3, "7f"}, - /* 0x80 */ {3, "80"}, {3, "81"}, {3, "82"}, {3, "83"}, - /* 0x84 */ {3, "84"}, {3, "85"}, {3, "86"}, {3, "87"}, - /* 0x88 */ {3, "88"}, {3, "89"}, {3, "8a"}, {3, "8b"}, - /* 0x8c */ {3, "8c"}, {3, "8d"}, {3, "8e"}, {3, "8f"}, - /* 0x90 */ {3, "90"}, {3, "91"}, {3, "92"}, {3, "93"}, - /* 0x94 */ {3, "94"}, {3, "95"}, {3, "96"}, {3, "97"}, - /* 0x98 */ {3, "98"}, {3, "99"}, {3, "9a"}, {3, "9b"}, - /* 0x9c */ {3, "9c"}, {3, "9d"}, {3, "9e"}, {3, "9f"}, - /* 0xa0 */ {3, "a0"}, {3, "a1"}, {3, "a2"}, {3, "a3"}, - /* 0xa4 */ {3, "a4"}, {3, "a5"}, {3, "a6"}, {3, "a7"}, - /* 0xa8 */ {3, "a8"}, {3, "a9"}, {3, "aa"}, {3, "ab"}, - /* 0xac */ {3, "ac"}, {3, "ad"}, {3, "ae"}, {3, "af"}, - /* 0xb0 */ {3, "b0"}, {3, "b1"}, {3, "b2"}, {3, "b3"}, - /* 0xb4 */ {3, "b4"}, {3, "b5"}, {3, "b6"}, {3, "b7"}, - /* 0xb8 */ {3, "b8"}, {3, "b9"}, {3, "ba"}, {3, "bb"}, - /* 0xbc */ {3, "bc"}, {3, "bd"}, {3, "be"}, {3, "bf"}, - /* 0xc0 */ {3, "c0"}, {3, "c1"}, {3, "c2"}, {3, "c3"}, - /* 0xc4 */ {3, "c4"}, {3, "c5"}, {3, "c6"}, {3, "c7"}, - /* 0xc8 */ {3, "c8"}, {3, "c9"}, {3, "ca"}, {3, "cb"}, - /* 0xcc */ {3, "cc"}, {3, "cd"}, {3, "ce"}, {3, "cf"}, - /* 0xd0 */ {3, "d0"}, {3, "d1"}, {3, "d2"}, {3, "d3"}, - /* 0xd4 */ {3, "d4"}, {3, "d5"}, {3, "d6"}, {3, "d7"}, - /* 0xd8 */ {3, "d8"}, {3, "d9"}, {3, "da"}, {3, "db"}, - /* 0xdc */ {3, "dc"}, {3, "dd"}, {3, "de"}, {3, "df"}, - /* 0xe0 */ {3, "e0"}, {3, "e1"}, {3, "e2"}, {3, "e3"}, - /* 0xe4 */ {3, "e4"}, {3, "e5"}, {3, "e6"}, {3, "e7"}, - /* 0xe8 */ {3, "e8"}, {3, "e9"}, {3, "ea"}, {3, "eb"}, - /* 0xec */ {3, "ec"}, {3, "ed"}, {3, "ee"}, {3, "ef"}, - /* 0xf0 */ {3, "f0"}, {3, "f1"}, {3, "f2"}, {3, "f3"}, - /* 0xf4 */ {3, "f4"}, {3, "f5"}, {3, "f6"}, {3, "f7"}, - /* 0xf8 */ {3, "f8"}, {3, "f9"}, {3, "fa"}, {3, "fb"}, + /* 0x00 */ {3, "00"}, {3, "01"}, {3, "02"}, {3, "03"}, + /* 0x04 */ {3, "04"}, {3, "05"}, {3, "06"}, {3, "07"}, + /* 0x08 */ {3, "08"}, {3, "09"}, {3, "0a"}, {3, "0b"}, + /* 0x0c */ {3, "0c"}, {3, "0d"}, {3, "0e"}, {3, "0f"}, + /* 0x10 */ {3, "10"}, {3, "11"}, {3, "12"}, {3, "13"}, + /* 0x14 */ {3, "14"}, {3, "15"}, {3, "16"}, {3, "17"}, + /* 0x18 */ {3, "18"}, {3, "19"}, {3, "1a"}, {3, "1b"}, + /* 0x1c */ {3, "1c"}, {3, "1d"}, {3, "1e"}, {3, "1f"}, + /* 0x20 */ {3, "20"}, {1, NULL}, {3, "22"}, {1, NULL}, + /* 0x24 */ {1, NULL}, {3, "25"}, {1, NULL}, {1, NULL}, + /* 0x28 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x2c */ {3, "2c"}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x30 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x34 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x38 */ {1, NULL}, {1, NULL}, {1, NULL}, {3, "3b"}, + /* 0x3c */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x40 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x44 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x48 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x4c */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x50 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x54 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x58 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x5c */ {3, "5c"}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x60 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x64 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x68 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x6c */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x70 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x74 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x78 */ {1, NULL}, {1, NULL}, {1, NULL}, {1, NULL}, + /* 0x7c */ {1, NULL}, {1, NULL}, {1, NULL}, {3, "7f"}, + /* 0x80 */ {3, "80"}, {3, "81"}, {3, "82"}, {3, "83"}, + /* 0x84 */ {3, "84"}, {3, "85"}, {3, "86"}, {3, "87"}, + /* 0x88 */ {3, "88"}, {3, "89"}, {3, "8a"}, {3, "8b"}, + /* 0x8c */ {3, "8c"}, {3, "8d"}, {3, "8e"}, {3, "8f"}, + /* 0x90 */ {3, "90"}, {3, "91"}, {3, "92"}, {3, "93"}, + /* 0x94 */ {3, "94"}, {3, "95"}, {3, "96"}, {3, "97"}, + /* 0x98 */ {3, "98"}, {3, "99"}, {3, "9a"}, {3, "9b"}, + /* 0x9c */ {3, "9c"}, {3, "9d"}, {3, "9e"}, {3, "9f"}, + /* 0xa0 */ {3, "a0"}, {3, "a1"}, {3, "a2"}, {3, "a3"}, + /* 0xa4 */ {3, "a4"}, {3, "a5"}, {3, "a6"}, {3, "a7"}, + /* 0xa8 */ {3, "a8"}, {3, "a9"}, {3, "aa"}, {3, "ab"}, + /* 0xac */ {3, "ac"}, {3, "ad"}, {3, "ae"}, {3, "af"}, + /* 0xb0 */ {3, "b0"}, {3, "b1"}, {3, "b2"}, {3, "b3"}, + /* 0xb4 */ {3, "b4"}, {3, "b5"}, {3, "b6"}, {3, "b7"}, + /* 0xb8 */ {3, "b8"}, {3, "b9"}, {3, "ba"}, {3, "bb"}, + /* 0xbc */ {3, "bc"}, {3, "bd"}, {3, "be"}, {3, "bf"}, + /* 0xc0 */ {3, "c0"}, {3, "c1"}, {3, "c2"}, {3, "c3"}, + /* 0xc4 */ {3, "c4"}, {3, "c5"}, {3, "c6"}, {3, "c7"}, + /* 0xc8 */ {3, "c8"}, {3, "c9"}, {3, "ca"}, {3, "cb"}, + /* 0xcc */ {3, "cc"}, {3, "cd"}, {3, "ce"}, {3, "cf"}, + /* 0xd0 */ {3, "d0"}, {3, "d1"}, {3, "d2"}, {3, "d3"}, + /* 0xd4 */ {3, "d4"}, {3, "d5"}, {3, "d6"}, {3, "d7"}, + /* 0xd8 */ {3, "d8"}, {3, "d9"}, {3, "da"}, {3, "db"}, + /* 0xdc */ {3, "dc"}, {3, "dd"}, {3, "de"}, {3, "df"}, + /* 0xe0 */ {3, "e0"}, {3, "e1"}, {3, "e2"}, {3, "e3"}, + /* 0xe4 */ {3, "e4"}, {3, "e5"}, {3, "e6"}, {3, "e7"}, + /* 0xe8 */ {3, "e8"}, {3, "e9"}, {3, "ea"}, {3, "eb"}, + /* 0xec */ {3, "ec"}, {3, "ed"}, {3, "ee"}, {3, "ef"}, + /* 0xf0 */ {3, "f0"}, {3, "f1"}, {3, "f2"}, {3, "f3"}, + /* 0xf4 */ {3, "f4"}, {3, "f5"}, {3, "f6"}, {3, "f7"}, + /* 0xf8 */ {3, "f8"}, {3, "f9"}, {3, "fa"}, {3, "fb"}, /* 0xfc */ {3, "fc"}, {3, "fd"}, {3, "fe"}, {3, "ff"} }; @@ -563,7 +563,7 @@ static const ByteKey cookie_enc[] = { */ void -Ns_UrlEncodingWarnUnencoded(const char *msg, const char *chars) +Ns_UrlEncodingWarnUnencoded(const char *msg, const char *chars) { static bool initialized = NS_FALSE; static bool mustBeEncoded[256]; @@ -587,7 +587,7 @@ Ns_UrlEncodingWarnUnencoded(const char *msg, const char *chars) */ mustBeEncoded[UCHAR('%')] = NS_FALSE; mustBeEncoded[UCHAR('=')] = NS_FALSE; - + for (i = 0u; i < 256u; i++) { if (path_enc[i].str == NULL) { mustBeEncoded[i] = NS_FALSE; @@ -604,8 +604,8 @@ Ns_UrlEncodingWarnUnencoded(const char *msg, const char *chars) if (mustBeEncoded[UCHAR(chars[i])]) { Ns_Log(Warning, "%s value '%s': byte with binary value 0x%.2x must be url encoded", msg, chars, UCHAR(chars[i])); - /* - * Just warn about the first invalid character + /* + * Just warn about the first invalid character */ break; } @@ -631,7 +631,7 @@ Ns_UrlEncodingWarnUnencoded(const char *msg, const char *chars) * A Tcl_Encoding. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ @@ -662,18 +662,18 @@ Ns_GetUrlEncoding(const char *charset) } else { /* In the current code, the URL is decoded via UrlPathDecode() *before* the NsConnThread() is started, so - connPtr will be normally NULL. - + connPtr will be normally NULL. + We need here an appropriate encoding to decode the URL. It would be nice to do here: - + NsServer *servPtr = NsGetServer(server); return servPtr->encoding.urlEncoding; - + However, "server" is not available here. Reading values from the config file would require "server" as well. - + Unfortunately, the general default for encoding opens a door for a path traversal attack with (invalid) UTF-8 characters. For example, ".." can be encoded via @@ -685,7 +685,7 @@ Ns_GetUrlEncoding(const char *charset) e.g. /etc/passwd from NaviServer. For more details, see Section "Canonicalization" in - http://www.cgisecurity.com/owasp/html/ch11s03.html + http://www.cgisecurity.com/owasp/html/ch11s03.html A simple approach to handle this attack is to fall back to utf-8 encodings and let Tcl do the UTF-8 @@ -694,7 +694,7 @@ Ns_GetUrlEncoding(const char *charset) -gustaf neumann */ encoding = NS_utf8Encoding; - } + } } return encoding; @@ -720,7 +720,8 @@ Ns_GetUrlEncoding(const char *charset) */ char * -Ns_UrlPathEncode(Ns_DString *dsPtr, const char *urlSegment, Tcl_Encoding encoding) +Ns_UrlPathEncode(Ns_DString *dsPtr, const char *urlSegment, + Tcl_Encoding encoding) { NS_NONNULL_ASSERT(dsPtr != NULL); NS_NONNULL_ASSERT(urlSegment != NULL); @@ -729,7 +730,8 @@ Ns_UrlPathEncode(Ns_DString *dsPtr, const char *urlSegment, Tcl_Encoding encodin } char * -Ns_UrlPathDecode(Ns_DString *dsPtr, const char *urlSegment, Tcl_Encoding encoding) +Ns_UrlPathDecode(Ns_DString *dsPtr, const char *urlSegment, + Tcl_Encoding encoding) { NS_NONNULL_ASSERT(dsPtr != NULL); NS_NONNULL_ASSERT(urlSegment != NULL); @@ -758,7 +760,8 @@ Ns_UrlPathDecode(Ns_DString *dsPtr, const char *urlSegment, Tcl_Encoding encodin */ char * -Ns_UrlQueryEncode(Ns_DString *dsPtr, const char *urlSegment, Tcl_Encoding encoding) +Ns_UrlQueryEncode(Ns_DString *dsPtr, const char *urlSegment, + Tcl_Encoding encoding) { NS_NONNULL_ASSERT(dsPtr != NULL); NS_NONNULL_ASSERT(urlSegment != NULL); @@ -767,7 +770,8 @@ Ns_UrlQueryEncode(Ns_DString *dsPtr, const char *urlSegment, Tcl_Encoding encodi } char * -Ns_UrlQueryDecode(Ns_DString *dsPtr, const char *urlSegment, Tcl_Encoding encoding) +Ns_UrlQueryDecode(Ns_DString *dsPtr, const char *urlSegment, + Tcl_Encoding encoding) { NS_NONNULL_ASSERT(dsPtr != NULL); NS_NONNULL_ASSERT(urlSegment != NULL); @@ -815,9 +819,9 @@ Ns_CookieDecode(Ns_DString *dsPtr, const char *cookie, Tcl_Encoding encoding) #ifdef RFC1738 return UrlDecode(dsPtr, cookie, encoding, 'q'); -#else +#else return UrlDecode(dsPtr, cookie, encoding, 'c'); -#endif +#endif } @@ -831,8 +835,8 @@ Ns_CookieDecode(Ns_DString *dsPtr, const char *cookie, Tcl_Encoding encoding) * Deprecated. * * Results: - * A pointer to the transformed urlSegment (which is part of the - * passed-in DString's memory) + * A pointer to the transformed urlSegment (which is part of the + * passed-in DString's memory) * * Side effects: * transformed input will be copied to given dstring. @@ -841,7 +845,8 @@ Ns_CookieDecode(Ns_DString *dsPtr, const char *cookie, Tcl_Encoding encoding) */ char * -Ns_EncodeUrlWithEncoding(Ns_DString *dsPtr, const char *urlSegment, Tcl_Encoding encoding) +Ns_EncodeUrlWithEncoding(Ns_DString *dsPtr, const char *urlSegment, + Tcl_Encoding encoding) { NS_NONNULL_ASSERT(dsPtr != NULL); NS_NONNULL_ASSERT(urlSegment != NULL); @@ -850,7 +855,8 @@ Ns_EncodeUrlWithEncoding(Ns_DString *dsPtr, const char *urlSegment, Tcl_Encoding } char * -Ns_EncodeUrlCharset(Ns_DString *dsPtr, const char *urlSegment, const char *charset) +Ns_EncodeUrlCharset(Ns_DString *dsPtr, const char *urlSegment, + const char *charset) { Tcl_Encoding encoding = Ns_GetUrlEncoding(charset); @@ -862,7 +868,8 @@ Ns_EncodeUrlCharset(Ns_DString *dsPtr, const char *urlSegment, const char *chars } char * -Ns_DecodeUrlWithEncoding(Ns_DString *dsPtr, const char *urlSegment, Tcl_Encoding encoding) +Ns_DecodeUrlWithEncoding(Ns_DString *dsPtr, const char *urlSegment, + Tcl_Encoding encoding) { NS_NONNULL_ASSERT(dsPtr != NULL); NS_NONNULL_ASSERT(urlSegment != NULL); @@ -871,7 +878,8 @@ Ns_DecodeUrlWithEncoding(Ns_DString *dsPtr, const char *urlSegment, Tcl_Encoding } char * -Ns_DecodeUrlCharset(Ns_DString *dsPtr, const char *urlSegment, const char *charset) +Ns_DecodeUrlCharset(Ns_DString *dsPtr, const char *urlSegment, + const char *charset) { Tcl_Encoding encoding = Ns_GetUrlEncoding(charset); @@ -896,7 +904,7 @@ Ns_DecodeUrlCharset(Ns_DString *dsPtr, const char *urlSegment, const char *chars * feature. * * Results: - * Tcl result. + * Tcl result. * * Side effects: * None. @@ -905,7 +913,8 @@ Ns_DecodeUrlCharset(Ns_DString *dsPtr, const char *urlSegment, const char *chars */ int -NsTclUrlEncodeObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) +NsTclUrlEncodeObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, + int objc, Tcl_Obj *CONST* objv) { int nargs, upperCase = 0, result = TCL_OK; char *charset = NULL; @@ -926,7 +935,7 @@ NsTclUrlEncodeObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc Ns_ObjvSpec args[] = { {"args", Ns_ObjvArgs, &nargs, NULL}, {NULL, NULL, NULL, NULL} - }; + }; if (Ns_ParseObjv(lopts, args, interp, 1, objc, objv) != NS_OK) { result = TCL_ERROR; @@ -940,7 +949,8 @@ NsTclUrlEncodeObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc } Ns_DStringInit(&ds); for (i = objc - nargs; i < objc; ++i) { - (void)UrlEncode(&ds, Tcl_GetString(objv[i]), encoding, part, (upperCase == 1)); + (void)UrlEncode(&ds, Tcl_GetString(objv[i]), encoding, part, + (upperCase == 1)); if (i + 1 < objc) { if (part == 'q') { Ns_DStringNAppend(&ds, "&", 1); @@ -951,7 +961,7 @@ NsTclUrlEncodeObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc } Tcl_DStringResult(interp, &ds); } - + return result; } @@ -965,7 +975,7 @@ NsTclUrlEncodeObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc * is not specified, query is assumed. * * Results: - * Tcl result. + * Tcl result. * * Side effects: * None. @@ -974,7 +984,8 @@ NsTclUrlEncodeObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc */ int -NsTclUrlDecodeObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) +NsTclUrlDecodeObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, + int objc, Tcl_Obj *CONST* objv) { int result = TCL_OK; char *charset = NULL, *chars = NULL; @@ -1002,13 +1013,15 @@ NsTclUrlDecodeObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc Ns_DString ds; Tcl_Encoding encoding = NULL; + assert(chars != NULL); + Ns_DStringInit(&ds); if (charset != NULL) { encoding = Ns_GetCharsetEncoding(charset); } else { encoding = Ns_GetUrlEncoding(NULL); } - + (void)UrlDecode(&ds, chars, encoding, part); Tcl_DStringResult(interp, &ds); } @@ -1024,8 +1037,8 @@ NsTclUrlDecodeObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc * Encode the given URL component according to part. * * Results: - * A pointer to the encoded string (which is part of the - * passed-in DString's memory) + * A pointer to the encoded string (which is part of the + * passed-in DString's memory) * * Side effects: * Encoded URL component will be copied to given dstring. @@ -1034,7 +1047,8 @@ NsTclUrlDecodeObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc */ static char * -UrlEncode(Ns_DString *dsPtr, const char *urlSegment, Tcl_Encoding encoding, char part, bool upperCase) +UrlEncode(Ns_DString *dsPtr, const char *urlSegment, Tcl_Encoding encoding, + char part, bool upperCase) { register int i, n; register char *q; @@ -1066,7 +1080,7 @@ UrlEncode(Ns_DString *dsPtr, const char *urlSegment, Tcl_Encoding encoding, char i = dsPtr->length; n = 0; for (p = urlSegment; *p != '\0'; p++) { - n += enc[UCHAR(*p)].len; + n += enc[UCHAR(*p)].len; } Ns_DStringSetLength(dsPtr, dsPtr->length + n); @@ -1101,7 +1115,7 @@ UrlEncode(Ns_DString *dsPtr, const char *urlSegment, Tcl_Encoding encoding, char if (encoding != NULL) { Tcl_DStringFree(&ds); } - + return dsPtr->string; } @@ -1130,9 +1144,9 @@ PercentDecode(char *dest, const char *source, char part) register const char *p = source; register int n = 0; static const int hex_code[] = { - /* 0x00 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - /* 0x10 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - /* 0x20 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 0x00 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 0x10 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 0x20 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x30 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /* 0x40 */ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x50 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, @@ -1155,7 +1169,7 @@ PercentDecode(char *dest, const char *source, char part) if (unlikely(p[0] == '%')) { /* * Decode percent code and make sure not to read date after - * the NULL character. + * the NULL character. */ c1 = p[1]; if (c1 != '\0') { @@ -1181,7 +1195,7 @@ PercentDecode(char *dest, const char *source, char part) } n++; } - /* + /* * Ensure the resulting string is null-terminated. */ *q = '\0'; @@ -1198,17 +1212,18 @@ PercentDecode(char *dest, const char *source, char part) * Decode the given URL component according to part. * * Results: - * A pointer to the dstring's value, containing the decoded - * URL. + * A pointer to the dstring's value, containing the decoded + * URL. * * Side effects: - * Decoded URL will be copied to given dstring. + * Decoded URL will be copied to given dstring. * *---------------------------------------------------------------------- */ static char * -UrlDecode(Ns_DString *dsPtr, const char *urlSegment, Tcl_Encoding encoding, char part) +UrlDecode(Ns_DString *dsPtr, const char *urlSegment, Tcl_Encoding encoding, + char part) { const char *firstCode; size_t inputLength; @@ -1227,7 +1242,7 @@ UrlDecode(Ns_DString *dsPtr, const char *urlSegment, Tcl_Encoding encoding, char * contains '%' or '+'. */ firstCode = strpbrk(urlSegment, "%+"); - + if (likely(firstCode == NULL) && (likely(encoding == NS_utf8Encoding) || (encoding == NULL) ) ) { @@ -1251,7 +1266,7 @@ UrlDecode(Ns_DString *dsPtr, const char *urlSegment, Tcl_Encoding encoding, char */ Ns_DStringSetLength(dsPtr, oldLength + (int)inputLength); decoded = dsPtr->string + oldLength; - + if (firstCode != NULL) { decodedLength = PercentDecode(decoded, urlSegment, part); } else { @@ -1262,12 +1277,13 @@ UrlDecode(Ns_DString *dsPtr, const char *urlSegment, Tcl_Encoding encoding, char if (likely(encoding != NULL)) { Tcl_DString ds; - (void)Tcl_ExternalToUtfDString(encoding, decoded, decodedLength, &ds); - + (void)Tcl_ExternalToUtfDString(encoding, decoded, decodedLength, + &ds); + Ns_DStringSetLength(dsPtr, oldLength); Ns_DStringAppend(dsPtr, Tcl_DStringValue(&ds)); Tcl_DStringFree(&ds); - + } else { /* * The length of dsPtr is now (oldLength + inputLength); diff --git a/nsd/urlspace.c b/nsd/urlspace.c index d24acc77..48e176f6 100644 --- a/nsd/urlspace.c +++ b/nsd/urlspace.c @@ -11,7 +11,7 @@ * * The Original Code is AOLserver Code and related documentation * distributed by AOL. - * + * * The Initial Developer of the Original Code is America Online, * Inc. Portions created by AOL are Copyright (C) 1999 America Online, * Inc. All Rights Reserved. @@ -54,7 +54,7 @@ * pointer to a Node, if data was registered for this specific branch. * 4. Node * A node stores URL-specific data, as well as a pointer to the - * cleanup function. + * cleanup function. * * Another data structure, called an Index, which is manipulated by the * Ns_Index API calls, is used by the urlspace code. An Index is an @@ -146,8 +146,8 @@ * since it passes all tests. */ -/* -#define __URLSPACE_OPTIMIZE__ +/* +#define __URLSPACE_OPTIMIZE__ */ /* @@ -255,7 +255,7 @@ typedef struct { typedef struct Junction { Ns_Index byname; - /* + /* * We've experimented with getting rid of this index because * it is like byname but in semi-reverse lexicographical * order. This optimization seems to work in all cases, but @@ -315,7 +315,7 @@ static void PrintSeq(const char *seq); static void TrieInit(Trie *triePtr) NS_GNUC_NONNULL(1); -static void TrieAdd(Trie *triePtr, char *seq, void *data, unsigned int flags, +static void TrieAdd(Trie *triePtr, char *seq, void *data, unsigned int flags, void (*deletefunc)(void *data)) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(3); @@ -452,7 +452,6 @@ Ns_UrlSpecificSet(const char *server, const char *method, const char *url, int i void *data, unsigned int flags, void (*deletefunc) (void *data)) { NsServer *servPtr; - Ns_DString ds; NS_NONNULL_ASSERT(server != NULL); NS_NONNULL_ASSERT(method != NULL); @@ -461,15 +460,19 @@ Ns_UrlSpecificSet(const char *server, const char *method, const char *url, int i servPtr = NsGetServer(server); - Ns_DStringInit(&ds); - MkSeq(&ds, method, url); - + if (likely(servPtr != NULL)) { + Ns_DString ds; + + Ns_DStringInit(&ds); + MkSeq(&ds, method, url); + #ifdef DEBUG - PrintSeq(ds.string); + PrintSeq(ds.string); #endif - - JunctionAdd(JunctionGet(servPtr, id), ds.string, data, flags, deletefunc); - Ns_DStringFree(&ds); + + JunctionAdd(JunctionGet(servPtr, id), ds.string, data, flags, deletefunc); + Ns_DStringFree(&ds); + } } @@ -555,27 +558,27 @@ NsUrlSpecificGet(NsServer *servPtr, const char *method, const char *url, int id, Ns_DString ds, *dsPtr = &ds; void *data; const Junction *junction; - + NS_NONNULL_ASSERT(servPtr != NULL); NS_NONNULL_ASSERT(method != NULL); NS_NONNULL_ASSERT(url != NULL); junction = JunctionGet(servPtr, id); - + Ns_DStringInit(dsPtr); MkSeq(dsPtr, method, url); #ifdef DEBUG fprintf(stderr, "NsUrlSpecificGet %s %s op %d\n", method, url, op); PrintSeq(dsPtr->string); -#endif +#endif switch (op) { - + case NS_URLSPACE_DEFAULT: data = JunctionFind(junction, dsPtr->string); break; - + case NS_URLSPACE_EXACT: data = JunctionFindExact(junction, dsPtr->string, flags); break; @@ -586,7 +589,7 @@ NsUrlSpecificGet(NsServer *servPtr, const char *method, const char *url, int id, */ data = JunctionFind(junction, dsPtr->string); break; - + default: /* * should never happen @@ -594,7 +597,7 @@ NsUrlSpecificGet(NsServer *servPtr, const char *method, const char *url, int id, data = NULL; break; } - + Ns_DStringFree(dsPtr); return data; @@ -624,7 +627,6 @@ Ns_UrlSpecificDestroy(const char *server, const char *method, const char *url, int id, unsigned int flags) { NsServer *servPtr; - Ns_DString ds; void *data = NULL; NS_NONNULL_ASSERT(server != NULL); @@ -633,15 +635,19 @@ Ns_UrlSpecificDestroy(const char *server, const char *method, const char *url, servPtr = NsGetServer(server); - Ns_DStringInit(&ds); - MkSeq(&ds, method, url); - if ((flags & NS_OP_RECURSE) != 0u) { - JunctionTruncBranch(JunctionGet(servPtr, id), ds.string); - data = NULL; - } else { - data = JunctionDeleteNode(JunctionGet(servPtr, id), ds.string, flags); + if (likely(servPtr != NULL)) { + Ns_DString ds; + + Ns_DStringInit(&ds); + MkSeq(&ds, method, url); + if ((flags & NS_OP_RECURSE) != 0u) { + JunctionTruncBranch(JunctionGet(servPtr, id), ds.string); + data = NULL; + } else { + data = JunctionDeleteNode(JunctionGet(servPtr, id), ds.string, flags); + } + Ns_DStringFree(&ds); } - Ns_DStringFree(&ds); return data; } @@ -690,7 +696,7 @@ Ns_UrlSpecificWalk(int id, const char *server, Ns_ArgProc func, Tcl_DString *dsP WalkTrie(&channelPtr->trie, func, dsPtr, stack, channelPtr->filter); } } - + static void WalkTrie(const Trie *triePtr, Ns_ArgProc func, Ns_DString *dsPtr, char **stack, const char *filter) @@ -724,10 +730,10 @@ WalkTrie(const Trie *triePtr, Ns_ArgProc func, /* * Restore stack position */ - + stack[depth] = NULL; } - + nodePtr = triePtr->node; if (nodePtr != NULL) { @@ -747,7 +753,7 @@ WalkTrie(const Trie *triePtr, Ns_ArgProc func, sprintf(buffer, "%p:", (void*)nodePtr); Tcl_DStringAppend(&subDs, buffer, -1); } -#endif +#endif if (stack[depth] == NULL) { Tcl_DStringAppendElement(&subDs, "/"); } else { @@ -760,7 +766,7 @@ WalkTrie(const Trie *triePtr, Ns_ArgProc func, } Tcl_DStringAppendElement(&subDs, elementDs.string); Tcl_DStringFree(&elementDs); - + } Ns_DStringVarAppend(&subDs, " ", filter, " ", (char *)0); @@ -771,14 +777,14 @@ WalkTrie(const Trie *triePtr, Ns_ArgProc func, if (nodePtr->dataInherit != NULL) { Tcl_DStringStartSublist(dsPtr); - Tcl_DStringAppend(dsPtr, subDs.string, -1); + Tcl_DStringAppend(dsPtr, subDs.string, subDs.length); Tcl_DStringAppendElement(dsPtr, "inherit"); (*func)(dsPtr, nodePtr->dataInherit); Tcl_DStringEndSublist(dsPtr); } if (nodePtr->dataNoInherit != NULL) { Tcl_DStringStartSublist(dsPtr); - Tcl_DStringAppend(dsPtr, subDs.string, -1); + Tcl_DStringAppend(dsPtr, subDs.string, subDs.length); Tcl_DStringAppendElement(dsPtr, "noinherit"); (*func)(dsPtr, nodePtr->dataNoInherit); Tcl_DStringEndSublist(dsPtr); @@ -1205,7 +1211,7 @@ TrieFind(const Trie *triePtr, char *seq, int *depthPtr) nodePtr = triePtr->node; ldepth = *depthPtr; -#ifdef DEBUG +#ifdef DEBUG fprintf(stderr, "... TrieFind seq '%s' nodePtr %p ldepth %d\n", seq, (void*)nodePtr, ldepth); #endif @@ -1217,7 +1223,7 @@ TrieFind(const Trie *triePtr, char *seq, int *depthPtr) } else { data = nodePtr->dataInherit; } -#ifdef DEBUG +#ifdef DEBUG fprintf(stderr, "... TrieFind seq '%s' nodePtr %p -> data %p\n", seq, (void*)nodePtr, data); #endif } @@ -1230,7 +1236,7 @@ TrieFind(const Trie *triePtr, char *seq, int *depthPtr) branchPtr = Ns_IndexFind(&triePtr->branches, seq); ldepth += 1; -#ifdef DEBUG +#ifdef DEBUG fprintf(stderr, "... TrieFind seq '%s' recurse on branch %p\n", seq, (void*)branchPtr); #endif if (branchPtr != NULL) { @@ -1319,7 +1325,7 @@ TrieFindExact(const Trie *triePtr, char *seq, unsigned int flags) * skip calling the delete function. * * Results: - * A pointer to the now-deleted data. + * A pointer to the now-deleted data. * * Side effects: * Data may be deleted. @@ -1393,8 +1399,8 @@ TrieDelete(const Trie *triePtr, char *seq, unsigned int flags) * Compare the filters of two channels. * * Results: - * 0: Not the case that one contains the other OR they both - * contain each other; 1: left contains right; -1: right contans + * 0: Not the case that one contains the other OR they both + * contain each other; 1: left contains right; -1: right contans * left. * * Side effects: @@ -1429,7 +1435,7 @@ CmpChannels(const Channel *const*leftPtrPtr, const Channel *const*rightPtrPtr) } else { result = 0; } - + return result; } @@ -1442,8 +1448,8 @@ CmpChannels(const Channel *const*leftPtrPtr, const Channel *const*rightPtrPtr) * Compare a key to a channel's filter. * * Results: - * 0: Not the case that one contains the other OR they both - * contain each other; 1: key contains filter; -1: filter + * 0: Not the case that one contains the other OR they both + * contain each other; 1: key contains filter; -1: filter * contains key. * * Side effects: @@ -1459,7 +1465,7 @@ CmpKeyWithChannel(const char *key, const Channel *const*channelPtrPtr) #ifdef DEBUG fprintf(stderr, "======= CmpKeyWithChannel %s\n", key); -#endif +#endif NS_NONNULL_ASSERT(key != NULL); NS_NONNULL_ASSERT(channelPtrPtr != NULL); @@ -1514,13 +1520,13 @@ CmpChannelsAsStrings(const Channel *const*leftPtrPtr, const Channel *const*right * * CmpKeyWithChannelAsStrings -- * - * Compare a string key to a channel's filter + * Compare a string key to a channel's filter * * Results: - * Same as NS_strcmp. + * Same as NS_strcmp. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ @@ -1552,7 +1558,7 @@ CmpKeyWithChannelAsStrings(const char *key, const Channel *const*channelPtrPtr) * Pointer to junction. * * Side effects: - * Will initialise the junction on first access. + * Will initialize the junction on first access. * *---------------------------------------------------------------------- */ @@ -1648,7 +1654,7 @@ JunctionTruncBranch(const Junction *juncPtr, char *seq) * will not be deleted but replaced. * * Results: - * None. + * None. * * Side effects: * Modifies seq, assuming @@ -1666,7 +1672,7 @@ JunctionAdd(Junction *juncPtr, char *seq, void *data, unsigned int flags, char *p; int depth; size_t l; - + NS_NONNULL_ASSERT(juncPtr != NULL); NS_NONNULL_ASSERT(seq != NULL); @@ -1693,7 +1699,6 @@ JunctionAdd(Junction *juncPtr, char *seq, void *data, unsigned int flags, * dsWord will eventually be used to set or find&reuse a channel * filter. */ - assert(p != NULL); if ((depth > 0) && (strchr(p, INTCHAR('*')) != NULL || strchr(p, INTCHAR('?')) != NULL )) { Ns_DStringAppend(&dsFilter, p); *p = '\0'; @@ -1711,7 +1716,7 @@ JunctionAdd(Junction *juncPtr, char *seq, void *data, unsigned int flags, fprintf(stderr, "--- Ns_IndexFind '%s' returned %p\n", dsFilter.string, (void *)channelPtr); #endif - /* + /* * If no channel is found, create a new channel and add it to the * list of channels in the junction. */ @@ -1728,7 +1733,7 @@ JunctionAdd(Junction *juncPtr, char *seq, void *data, unsigned int flags, } Ns_DStringFree(&dsFilter); - /* + /* * Now we need to create a sequence of branches in the trie (if no * appropriate sequence already exists) and a node at the end of it. * TrieAdd will do that. @@ -1775,7 +1780,7 @@ JunctionFind(const Junction *juncPtr, char *seq) * After this loop, p will point at the last element in the * sequence. */ - + for (p = seq; p[l = NS_strlen(p) + 1u] != '\0'; p += l) { ; } @@ -1794,7 +1799,7 @@ JunctionFind(const Junction *juncPtr, char *seq) if (l == 0u) { return NULL; } - + /* * For __URLSPACE_OPTIMIZE__ * Basically if we use the optimize, let's reverse the order @@ -1807,12 +1812,12 @@ JunctionFind(const Junction *juncPtr, char *seq) #ifndef __URLSPACE_OPTIMIZE__ for (i = 0u; i < l; i++) { bool doit; - + channelPtr = Ns_IndexEl(&juncPtr->byuse, i); #else for (i = l; i > 0u; i--) { bool doit; - + channelPtr = Ns_IndexEl(&juncPtr->byname, i - 1u); #endif @@ -1821,7 +1826,7 @@ JunctionFind(const Junction *juncPtr, char *seq) || (NS_Tcl_StringMatch(p, channelPtr->filter) == 1) ); -#ifdef DEBUG +#ifdef DEBUG fprintf(stderr, "JunctionFind: compare filter '%s' with channel filter '%s' => %d\n", p, channelPtr->filter, doit); #endif @@ -1952,7 +1957,7 @@ JunctionFindExact(const Junction *juncPtr, char *seq, unsigned int flags) } /* - * Now go to the channel with the "*" filter and look there for + * Now go to the channel with the "*" filter and look there for * an exact match: */ @@ -1980,10 +1985,10 @@ JunctionFindExact(const Junction *juncPtr, char *seq, unsigned int flags) * * JunctionDeleteNode -- * - * Delete a node from a junction matching a sequence + * Delete a node from a junction matching a sequence * * Results: - * A pointer to the deleted node + * A pointer to the deleted node * * Side effects: * Seq will be modified. @@ -2133,7 +2138,7 @@ MkSeq(Ns_DString *dsPtr, const char *method, const char *url) * *---------------------------------------------------------------------- */ - + static void PrintSeq(const char *seq) { @@ -2163,14 +2168,14 @@ PrintSeq(const char *seq) * returns a TCL_ERROR in such cases. * * Results: - * Tcl result. + * Tcl result. * * Side effects: * Updating the used tclUrlSpaces. * *---------------------------------------------------------------------- */ - + static int AllocTclUrlSpaceId(Tcl_Interp *interp, int *idPtr) { @@ -2178,7 +2183,7 @@ AllocTclUrlSpaceId(Tcl_Interp *interp, int *idPtr) NS_NONNULL_ASSERT(interp != NULL); NS_NONNULL_ASSERT(idPtr != NULL); - + if (nextid < MAX_URLSPACES-1) { *idPtr = Ns_UrlSpecificAlloc(); tclUrlSpaces[*idPtr] = NS_TRUE; @@ -2187,7 +2192,7 @@ AllocTclUrlSpaceId(Tcl_Interp *interp, int *idPtr) Ns_TclPrintfResult(interp, "maximum number of urlspaces (%d) reached", MAX_URLSPACES); result = TCL_ERROR; } - + return result; } @@ -2202,13 +2207,13 @@ AllocTclUrlSpaceId(Tcl_Interp *interp, int *idPtr) * interface). * * Results: - * Tcl result. + * Tcl result. * * Side effects: * Potentially allocating a new urlspace id. * *---------------------------------------------------------------------- - */ + */ static int CheckTclUrlSpaceId(Tcl_Interp *interp, NsServer *servPtr, int *idPtr) { @@ -2219,28 +2224,28 @@ CheckTclUrlSpaceId(Tcl_Interp *interp, NsServer *servPtr, int *idPtr) NS_NONNULL_ASSERT(idPtr != NULL); if (*idPtr == -1) { - + Ns_MutexLock(&servPtr->urlspace.lock); if (defaultTclUrlSpaceId < 0) { - /* + /* * Allocate a default Tcl urlspace id */ result = AllocTclUrlSpaceId(interp, &defaultTclUrlSpaceId); } Ns_MutexUnlock(&servPtr->urlspace.lock); - + if (result == TCL_OK) { *idPtr = defaultTclUrlSpaceId; } - + } else if ((*idPtr < 0) || (*idPtr >= MAX_URLSPACES) || (tclUrlSpaces[*idPtr] == NS_FALSE)) { Ns_TclPrintfResult(interp, "provided urlspace id %d is invalid", *idPtr); result = TCL_ERROR; } - + return result; } - + /* *---------------------------------------------------------------------- @@ -2275,7 +2280,7 @@ WalkCallback(Ns_DString *dsPtr, const void *arg) * Implements the "ns_urlspace get" command. * * Results: - * Tcl result. + * Tcl result. * * Side effects: * Depends on subcommand. @@ -2296,12 +2301,12 @@ UrlSpaceGetObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj * {"-key", Ns_ObjvString, &key, NULL}, {"-noinherit", Ns_ObjvBool, &noinherit, INT2PTR(NS_TRUE)}, {NULL, NULL, NULL, NULL} - }; + }; Ns_ObjvSpec args[] = { {"URL", Ns_ObjvString, &url, NULL}, {NULL, NULL, NULL, NULL} }; - + if (Ns_ParseObjv(lopts, args, interp, 2, objc, objv) != NS_OK) { result = TCL_ERROR; @@ -2321,7 +2326,7 @@ UrlSpaceGetObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj * if (noinherit == (int)NS_TRUE) { exact = (int)NS_TRUE; } - + if (exact == (int)NS_TRUE) { op = NS_URLSPACE_EXACT; if (noinherit) { @@ -2331,7 +2336,7 @@ UrlSpaceGetObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj * op = NS_URLSPACE_DEFAULT; } -#ifdef DEBUG +#ifdef DEBUG fprintf(stderr, "=== GET id %d key %s url %s op %d\n", id, key, url, op); #endif Ns_MutexLock(&servPtr->urlspace.lock); @@ -2351,7 +2356,7 @@ UrlSpaceGetObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj * * Implements the "ns_urlspace list" command. * * Results: - * Tcl result. + * Tcl result. * * Side effects: * Depends on subcommand. @@ -2367,8 +2372,8 @@ UrlSpaceListObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj Ns_ObjvSpec lopts[] = { {"-id", Ns_ObjvInt, &id, NULL}, {NULL, NULL, NULL, NULL} - }; - + }; + if (Ns_ParseObjv(lopts, NULL, interp, 2, objc, objv) != NS_OK) { result = TCL_ERROR; @@ -2379,7 +2384,7 @@ UrlSpaceListObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj Tcl_DString ds, *dsPtr = &ds; Ns_DStringInit(dsPtr); - + Ns_MutexLock(&servPtr->urlspace.lock); Ns_UrlSpecificWalk(id, servPtr->server, WalkCallback, dsPtr); Ns_MutexUnlock(&servPtr->urlspace.lock); @@ -2388,7 +2393,7 @@ UrlSpaceListObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj } return result; -} +} /* *---------------------------------------------------------------------- @@ -2398,7 +2403,7 @@ UrlSpaceListObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj * Implements the "ns_urlspace new" command. * * Results: - * Tcl result. + * Tcl result. * * Side effects: * Depends on subcommand. @@ -2411,21 +2416,21 @@ UrlSpaceNewObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj * const NsInterp *itPtr = clientData; NsServer *servPtr = itPtr->servPtr; int result = TCL_OK; - + if (Ns_ParseObjv(NULL, NULL, interp, 2, objc, objv) != NS_OK) { result = TCL_ERROR; } else { int id = -1; - + Ns_MutexLock(&servPtr->urlspace.lock); result = AllocTclUrlSpaceId(interp, &id); Ns_MutexUnlock(&servPtr->urlspace.lock); - + if (likely(result == TCL_OK)) { Tcl_SetObjResult(interp, Tcl_NewIntObj(id)); } } - + return result; } @@ -2438,7 +2443,7 @@ UrlSpaceNewObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj * * Implements the "ns_urlspace set" command. * * Results: - * Tcl result. + * Tcl result. * * Side effects: * Depends on subcommand. @@ -2457,30 +2462,30 @@ UrlSpaceSetObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj * {"-key", Ns_ObjvString, &key, NULL}, {"-noinherit", Ns_ObjvBool, &noinherit, INT2PTR(NS_TRUE)}, {NULL, NULL, NULL, NULL} - }; + }; Ns_ObjvSpec args[] = { {"URL", Ns_ObjvString, &url, NULL}, {"data", Ns_ObjvString, &data, NULL}, {NULL, NULL, NULL, NULL} }; - + if (Ns_ParseObjv(lopts, args, interp, 2, objc, objv) != NS_OK) { result = TCL_ERROR; } else if (CheckTclUrlSpaceId(interp, servPtr, &id) != TCL_OK) { result = TCL_ERROR; - + } else if (NS_strlen(key) < 1u) { Ns_TclPrintfResult(interp, "provided key must be at least one character"); result = TCL_ERROR; - + } else { unsigned int flags = 0u; if (noinherit != 0) { flags |= NS_OP_NOINHERIT; } -#ifdef DEBUG +#ifdef DEBUG fprintf(stderr, "=== SET use id %d\n", id); #endif Ns_MutexLock(&servPtr->urlspace.lock); @@ -2500,7 +2505,7 @@ UrlSpaceSetObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj * * Implements the "ns_urlspace unset" command. * * Results: - * Tcl result. + * Tcl result. * * Side effects: * Depends on subcommand. @@ -2521,12 +2526,12 @@ UrlSpaceUnsetObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj {"-noinherit", Ns_ObjvBool, &noinherit, INT2PTR(NS_TRUE)}, {"-recurse", Ns_ObjvBool, &recurse, INT2PTR(NS_TRUE)}, {NULL, NULL, NULL, NULL} - }; + }; Ns_ObjvSpec args[] = { {"URL", Ns_ObjvString, &url, NULL}, {NULL, NULL, NULL, NULL} }; - + if (Ns_ParseObjv(lopts, args, interp, 2, objc, objv) != NS_OK) { result = TCL_ERROR; @@ -2536,11 +2541,11 @@ UrlSpaceUnsetObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj } else if (NS_strlen(key) < 1u) { Ns_TclPrintfResult(interp, "the provided key must contain at least one character"); result = TCL_ERROR; - + } else { const char *data; unsigned int flags = 0u; - + if (noinherit == (int)NS_TRUE) { flags |= NS_OP_NOINHERIT; } @@ -2550,7 +2555,7 @@ UrlSpaceUnsetObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj Ns_Log(Warning, "flag -noinherit is ignored"); } } - + Ns_MutexLock(&servPtr->urlspace.lock); data = Ns_UrlSpecificDestroy(servPtr->server, key, url, id, flags); Ns_MutexUnlock(&servPtr->urlspace.lock); @@ -2569,7 +2574,7 @@ UrlSpaceUnsetObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj * Implements the ns_urlspace command. * * Results: - * Tcl result. + * Tcl result. * * Side effects: * Depends on subcommand. diff --git a/nsdb/dbtcl.c b/nsdb/dbtcl.c index 9a39b624..e40d7190 100644 --- a/nsdb/dbtcl.c +++ b/nsdb/dbtcl.c @@ -54,14 +54,14 @@ static int DbFail(Tcl_Interp *interp, Ns_DbHandle *handle, const char *cmd) static void EnterDbHandle(InterpData *idataPtr, Tcl_Interp *interp, Ns_DbHandle *handle, Tcl_Obj *listObj) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(3) NS_GNUC_NONNULL(4); static int DbGetHandle(InterpData *idataPtr, Tcl_Interp *interp, const char *handleId, - Ns_DbHandle **handle, Tcl_HashEntry **hPtrPtr); + Ns_DbHandle **handle, Tcl_HashEntry **hPtrPtr); static Tcl_InterpDeleteProc FreeData; -static Tcl_ObjCmdProc - DbConfigPathObjCmd, +static Tcl_ObjCmdProc + DbConfigPathObjCmd, DbErrorCodeObjCmd, - DbErrorMsgObjCmd, - DbObjCmd, - GetCsvObjCmd, + DbErrorMsgObjCmd, + DbObjCmd, + GetCsvObjCmd, PoolDescriptionObjCmd, QuoteListToListObjCmd; static int ErrorObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv, char cmd); @@ -97,7 +97,7 @@ Ns_TclDbGetHandle(Tcl_Interp *interp, const char *handleId, Ns_DbHandle **handle idataPtr = Tcl_GetAssocData(interp, datakey, NULL); if (idataPtr == NULL) { - result = TCL_ERROR; + result = TCL_ERROR; } else { result = DbGetHandle(idataPtr, interp, handleId, handlePtr, NULL); } @@ -175,7 +175,7 @@ NsDbReleaseHandles(Tcl_Interp *interp, const void *UNUSED(arg)) const Tcl_HashEntry *hPtr = Tcl_FirstHashEntry(&idataPtr->dbs, &search); while (hPtr != NULL) { - Ns_DbHandle *handlePtr = Tcl_GetHashValue(hPtr); + Ns_DbHandle *handlePtr = Tcl_GetHashValue(hPtr); Ns_DbPoolPutHandle(handlePtr); hPtr = Tcl_NextHashEntry(&search); @@ -207,7 +207,7 @@ NsDbReleaseHandles(Tcl_Interp *interp, const void *UNUSED(arg)) static int DbObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) { - InterpData *idataPtr = clientData; + InterpData *idataPtr = clientData; char tmpbuf[32] = ""; const char *pool = NULL; int cmd, nrows; @@ -218,18 +218,18 @@ DbObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* ob enum { POOLS, BOUNCEPOOL, GETHANDLE, EXCEPTION, POOLNAME, - PASSWORD, USER, DATASOURCE, DISCONNECT, DBTYPE, DRIVER, CANCEL, ROWCOUNT, - BINDROW, FLUSH, RELEASEHANDLE, RESETHANDLE, CONNECTED, SP_EXEC, - SP_GETPARAMS, SP_RETURNCODE, GETROW, DML, ONE_ROW, ZERO_OR_ONE_ROW, EXEC, - SELECT, SP_START, INTERPRETSQLFILE, VERBOSE, SETEXCEPTION, SP_SETPARAM, + PASSWORD, USER, DATASOURCE, DISCONNECT, DBTYPE, DRIVER, CANCEL, ROWCOUNT, + BINDROW, FLUSH, RELEASEHANDLE, RESETHANDLE, CONNECTED, SP_EXEC, + SP_GETPARAMS, SP_RETURNCODE, GETROW, DML, ONE_ROW, ZERO_OR_ONE_ROW, EXEC, + SELECT, SP_START, INTERPRETSQLFILE, VERBOSE, SETEXCEPTION, SP_SETPARAM, STATS, LOGMINDURATION, SESSIONID }; static const char *const subcmd[] = { "pools", "bouncepool", "gethandle", "exception", "poolname", - "password", "user", "datasource", "disconnect", "dbtype", "driver", "cancel", "rowcount", - "bindrow", "flush", "releasehandle", "resethandle", "connected", "sp_exec", - "sp_getparams", "sp_returncode", "getrow", "dml", "1row", "0or1row", "exec", - "select", "sp_start", "interpretsqlfile", "verbose", "setexception", "sp_setparam", + "password", "user", "datasource", "disconnect", "dbtype", "driver", "cancel", "rowcount", + "bindrow", "flush", "releasehandle", "resethandle", "connected", "sp_exec", + "sp_getparams", "sp_returncode", "getrow", "dml", "1row", "0or1row", "exec", + "select", "sp_start", "interpretsqlfile", "verbose", "setexception", "sp_setparam", "stats", "logminduration", "session_id", NULL }; @@ -262,20 +262,20 @@ DbObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* ob break; case BOUNCEPOOL: - if (objc != 3) { - Tcl_WrongNumArgs(interp, 2, objv, "pool"); + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "pool"); result = TCL_ERROR; - } else if (Ns_DbBouncePool(Tcl_GetString(objv[2])) == NS_ERROR) { - Ns_TclPrintfResult(interp, "could not bounce: %s", Tcl_GetString(objv[2])); - result = TCL_ERROR; - } + } else if (Ns_DbBouncePool(Tcl_GetString(objv[2])) == NS_ERROR) { + Ns_TclPrintfResult(interp, "could not bounce: %s", Tcl_GetString(objv[2])); + result = TCL_ERROR; + } break; case GETHANDLE: { - int nhandles = 1; + int nhandles = 1; Ns_Time *timeoutPtr = NULL; - Ns_DbHandle **handlesPtrPtr; + Ns_DbHandle **handlesPtrPtr; Ns_ReturnCode status; char *poolString = NULL; Ns_ObjvSpec opts[] = { @@ -284,7 +284,7 @@ DbObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* ob {NULL, NULL, NULL, NULL} }; Ns_ObjvSpec args[] = { - {"?pool", Ns_ObjvString, &poolString, NULL}, + {"?pool", Ns_ObjvString, &poolString, NULL}, {"?nhandles", Ns_ObjvInt, &nhandles, NULL}, {NULL, NULL, NULL, NULL} }; @@ -293,13 +293,13 @@ DbObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* ob return TCL_ERROR; } - /* - * Determine the pool and requested number of handles - * from the remaining args. - */ + /* + * Determine the pool and requested number of handles + * from the remaining args. + */ - if (poolString == NULL) { - pool = Ns_DbPoolDefault(idataPtr->server); + if (poolString == NULL) { + pool = Ns_DbPoolDefault(idataPtr->server); if (pool == NULL) { Ns_TclPrintfResult(interp, "no defaultpool configured"); return TCL_ERROR; @@ -307,16 +307,16 @@ DbObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* ob } else { pool = (const char *)poolString; } - + if (Ns_DbPoolAllowable(idataPtr->server, pool) == NS_FALSE) { Ns_TclPrintfResult(interp, "no access to pool: \"%s\"", pool); return TCL_ERROR; } - if (nhandles <= 0) { + if (nhandles <= 0) { Ns_TclPrintfResult(interp, "invalid nhandles %d: should be greater than 0.", nhandles); return TCL_ERROR; - } - + } + /* * When timeout is specified as 0 (or 0:0) then treat is as * non-specified (blocking). @@ -325,45 +325,45 @@ DbObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* ob timeoutPtr = NULL; } - /* + /* * Allocate handles and enter them into Tcl. */ - if (nhandles == 1) { - handlesPtrPtr = &handlePtr; - } else { - handlesPtrPtr = ns_malloc((size_t)nhandles * sizeof(Ns_DbHandle *)); - } - status = Ns_DbPoolTimedGetMultipleHandles(handlesPtrPtr, pool, - nhandles, timeoutPtr); - if (status == NS_OK) { - int i; + if (nhandles == 1) { + handlesPtrPtr = &handlePtr; + } else { + handlesPtrPtr = ns_malloc((size_t)nhandles * sizeof(Ns_DbHandle *)); + } + status = Ns_DbPoolTimedGetMultipleHandles(handlesPtrPtr, pool, + nhandles, timeoutPtr); + if (status == NS_OK) { + int i; Tcl_Obj *listObj = Tcl_NewListObj(0, NULL); - for (i = 0; i < nhandles; ++i) { - EnterDbHandle(idataPtr, interp, *(handlesPtrPtr + i), listObj); + for (i = 0; i < nhandles; ++i) { + EnterDbHandle(idataPtr, interp, *(handlesPtrPtr + i), listObj); } Tcl_SetObjResult(interp, listObj); - } - if (handlesPtrPtr != &handlePtr) { - ns_free(handlesPtrPtr); - } - if (status != NS_TIMEOUT && status != NS_OK) { - Ns_TclPrintfResult(interp, - "could not allocate %d handle%s from pool \"%s\"", - nhandles, - nhandles > 1 ? "s" : "", - pool); - result = TCL_ERROR; - } - break; + } + if (handlesPtrPtr != &handlePtr) { + ns_free(handlesPtrPtr); + } + if (status != NS_TIMEOUT && status != NS_OK) { + Ns_TclPrintfResult(interp, + "could not allocate %d handle%s from pool \"%s\"", + nhandles, + nhandles > 1 ? "s" : "", + pool); + result = TCL_ERROR; + } + break; } case LOGMINDURATION: { Ns_Time *minDurationPtr = NULL; char *poolString; Ns_ObjvSpec args[] = { - {"?pool", Ns_ObjvString, &poolString, NULL}, + {"?pool", Ns_ObjvString, &poolString, NULL}, {"?minduration", Ns_ObjvTime, &minDurationPtr, NULL}, {NULL, NULL, NULL, NULL} }; @@ -377,7 +377,7 @@ DbObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* ob */ Tcl_SetObjResult(interp, Ns_DbListMinDurations(interp, idataPtr->server)); - } else if (minDurationPtr == NULL) { + } else if (minDurationPtr == NULL) { pool = (const char *)poolString; /* * In this case, minduration was not given, return the @@ -401,7 +401,7 @@ DbObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* ob Tcl_SetObjResult(interp, Ns_TclNewTimeObj(minDurationPtr)); } } - + break; } @@ -415,7 +415,7 @@ DbObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* ob } else { Tcl_Obj *listObj = Tcl_NewListObj(0, NULL); - + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(handlePtr->cExceptionCode, -1)); Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(handlePtr->dsExceptionMsg.string, -1)); Tcl_SetObjResult(interp, listObj); @@ -448,7 +448,7 @@ DbObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* ob case SP_EXEC: /* fall through */ case SP_GETPARAMS: /* fall through */ case SP_RETURNCODE: /* fall through */ - case SESSIONID: + case SESSIONID: if (objc < 3) { Tcl_WrongNumArgs(interp, 2, objv, "dbId"); @@ -460,7 +460,7 @@ DbObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* ob Ns_DStringFree(&handlePtr->dsExceptionMsg); handlePtr->cExceptionCode[0] = '\0'; - /* + /* * The following commands require just the handle. */ @@ -478,11 +478,11 @@ DbObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* ob break; case DATASOURCE: - Tcl_SetObjResult(interp, Tcl_NewStringObj(handlePtr->datasource, -1)); + Tcl_SetObjResult(interp, Tcl_NewStringObj(handlePtr->datasource, -1)); break; case DISCONNECT: - NsDbDisconnect(handlePtr); + NsDbDisconnect(handlePtr); break; case DBTYPE: @@ -520,58 +520,60 @@ DbObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* ob break; case RELEASEHANDLE: - Tcl_DeleteHashEntry(hPtr); - Ns_DbPoolPutHandle(handlePtr); + Tcl_DeleteHashEntry(hPtr); + Ns_DbPoolPutHandle(handlePtr); break; case RESETHANDLE: - if (Ns_DbResetHandle(handlePtr) != NS_OK) { - result = DbFail(interp, handlePtr, Tcl_GetString(objv[1])); - } else { + if (Ns_DbResetHandle(handlePtr) != NS_OK) { + result = DbFail(interp, handlePtr, Tcl_GetString(objv[1])); + } else { Tcl_SetObjResult(interp, Tcl_NewIntObj(NS_OK)); } break; case CONNECTED: - Tcl_SetObjResult(interp, Tcl_NewBooleanObj(handlePtr->connected)); + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(handlePtr->connected)); break; case SESSIONID: { char idstr[TCL_INTEGER_SPACE + 4]; - - snprintf(idstr, sizeof(idstr), "sid%" PRIuPTR, NsDbGetSessionId(handlePtr)); - Tcl_SetObjResult(interp, Tcl_NewStringObj(idstr, -1)); + int length; + + memcpy(idstr, "sid", 3u); + length = ns_uint64toa(&idstr[3], (uint64_t)NsDbGetSessionId(handlePtr)); + Tcl_SetObjResult(interp, Tcl_NewStringObj(idstr, length + 3)); } break; - + case SP_EXEC: - switch (Ns_DbSpExec(handlePtr)) { - case NS_DML: - Tcl_SetObjResult(interp, Tcl_NewStringObj("NS_DML", 6)); - break; - case NS_ROWS: - Tcl_SetObjResult(interp, Tcl_NewStringObj("NS_ROWS", 7)); - break; - default: - result = DbFail(interp, handlePtr, Tcl_GetString(objv[1])); - } + switch (Ns_DbSpExec(handlePtr)) { + case NS_DML: + Tcl_SetObjResult(interp, Tcl_NewStringObj("NS_DML", 6)); + break; + case NS_ROWS: + Tcl_SetObjResult(interp, Tcl_NewStringObj("NS_ROWS", 7)); + break; + default: + result = DbFail(interp, handlePtr, Tcl_GetString(objv[1])); + } break; case SP_GETPARAMS: - rowPtr = Ns_DbSpGetParams(handlePtr); - if (rowPtr == NULL) { - result = DbFail(interp, handlePtr, Tcl_GetString(objv[1])); + rowPtr = Ns_DbSpGetParams(handlePtr); + if (rowPtr == NULL) { + result = DbFail(interp, handlePtr, Tcl_GetString(objv[1])); - } else if (unlikely(Ns_TclEnterSet(interp, rowPtr, NS_TCL_SET_DYNAMIC) != TCL_OK)) { + } else if (unlikely(Ns_TclEnterSet(interp, rowPtr, NS_TCL_SET_DYNAMIC) != TCL_OK)) { result = TCL_ERROR; } break; case SP_RETURNCODE: - if (Ns_DbSpReturnCode(handlePtr, tmpbuf, 32) != NS_OK) { + if (Ns_DbSpReturnCode(handlePtr, tmpbuf, 32) != NS_OK) { result = DbFail(interp, handlePtr, Tcl_GetString(objv[1])); - } else { + } else { Tcl_SetObjResult(interp, Tcl_NewStringObj(tmpbuf, -1)); } break; @@ -591,22 +593,22 @@ DbObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* ob case SP_START: case INTERPRETSQLFILE: - /* + /* * The following commands require a 3rd argument. - */ + */ if (objc != 4) { - if (cmd == INTERPRETSQLFILE) { + if (cmd == INTERPRETSQLFILE) { Tcl_WrongNumArgs(interp, 2, objv, "dbId sqlfile"); - } else if (cmd == GETROW) { + } else if (cmd == GETROW) { Tcl_WrongNumArgs(interp, 2, objv, "dbId row"); - } else { + } else { Tcl_WrongNumArgs(interp, 2, objv, "dbId sql"); - } + } return TCL_ERROR; - } + } if (DbGetHandle(idataPtr, interp, Tcl_GetString(objv[2]), &handlePtr, &hPtr) != TCL_OK) { return TCL_ERROR; @@ -668,9 +670,9 @@ DbObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* ob break; case SP_START: - if (Ns_DbSpStart(handlePtr, Tcl_GetString(objv[3])) != NS_OK) { - result = DbFail(interp, handlePtr, Tcl_GetString(objv[1])); - } else { + if (Ns_DbSpStart(handlePtr, Tcl_GetString(objv[3])) != NS_OK) { + result = DbFail(interp, handlePtr, Tcl_GetString(objv[1])); + } else { Tcl_SetObjResult(interp, Tcl_NewIntObj(0)); } break; @@ -682,7 +684,7 @@ DbObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* ob break; - case GETROW: + case GETROW: if (Ns_TclGetSet2(interp, Tcl_GetString(objv[3]), &rowPtr) != TCL_OK) { return TCL_ERROR; } @@ -738,9 +740,9 @@ DbObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* ob } else { const char *code; int codeLen; - + assert(handlePtr != NULL); - + code = Tcl_GetStringFromObj(objv[3], &codeLen); if (codeLen > 5) { Ns_TclPrintfResult(interp, "code \"%s"" more than 5 characters", code); @@ -757,14 +759,14 @@ DbObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* ob result = TCL_ERROR; } else { const char *arg5 = Tcl_GetString(objv[5]); - + if (!STREQ(arg5, "in") && !STREQ(arg5, "out")) { Ns_TclPrintfResult(interp, "inout parameter of setparam must " "be \"in\" or \"out\""); result = TCL_ERROR; } else { assert(handlePtr != NULL); - + if (Ns_DbSpSetParam(handlePtr, Tcl_GetString(objv[3]), Tcl_GetString(objv[4]), @@ -867,7 +869,7 @@ DbConfigPathObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj } else { const InterpData *idataPtr = clientData; const char *section = Ns_ConfigGetPath(idataPtr->server, NULL, "db", (char *)0); - + Tcl_SetObjResult(interp, Tcl_NewStringObj(section, -1)); } return result; @@ -894,7 +896,7 @@ static int PoolDescriptionObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) { int result = TCL_OK; - + if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "poolname"); result = TCL_ERROR; @@ -939,12 +941,12 @@ QuoteListToListObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int obj Ns_DStringInit(&ds); quotelist = Tcl_GetString(objv[1]); inquotes = NS_FALSE; - + while (*quotelist != '\0') { if (CHARTYPE(space, *quotelist) != 0 && !inquotes) { if (ds.length != 0) { Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(ds.string, ds.length)); - Ns_DStringTrunc(&ds, 0); + Ns_DStringSetLength(&ds, 0); } while (CHARTYPE(space, *quotelist) != 0) { quotelist++; @@ -956,7 +958,7 @@ QuoteListToListObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int obj if (inquotes) { /* Finish element */ Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(ds.string, ds.length)); - Ns_DStringTrunc(&ds, 0); + Ns_DStringSetLength(&ds, 0); inquotes = NS_FALSE; } else { /* Start element */ @@ -1000,7 +1002,7 @@ GetCsvObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_Ob { int result = TCL_OK; char *delimiter = (char *)",", *fileId, *varName; - Tcl_Channel chan; + Tcl_Channel chan; Ns_ObjvSpec opts[] = { {"-delimiter", Ns_ObjvString, &delimiter, NULL}, {"--", Ns_ObjvBreak, NULL, NULL}, @@ -1043,7 +1045,7 @@ GetCsvObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_Ob inquote = NS_FALSE; quoted = NS_FALSE; blank = NS_TRUE; - + p = line.string; while (*p != '\0') { c = *p++; @@ -1077,14 +1079,14 @@ GetCsvObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_Ob break; } } -#endif +#endif break; } if (c == '"') { inquote = NS_TRUE; quoted = NS_TRUE; blank = NS_FALSE; - } else if ((c == '\r') + } else if ((c == '\r') || ((elem.length == 0) && (CHARTYPE(space, c) != 0)) ) { continue; @@ -1093,7 +1095,7 @@ GetCsvObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_Ob (void) Ns_StrTrimRight(elem.string); } Tcl_DStringAppendElement(&cols, elem.string); - Tcl_DStringTrunc(&elem, 0); + Tcl_DStringSetLength(&elem, 0); ncols++; quoted = NS_FALSE; } else { @@ -1113,7 +1115,7 @@ GetCsvObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_Ob Tcl_DStringFree(&line); Tcl_DStringFree(&cols); Tcl_DStringFree(&elem); - + if (value == NULL) { result = TCL_ERROR; } else { @@ -1141,15 +1143,15 @@ GetCsvObjCmd(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_Ob static int DbGetHandle(InterpData *idataPtr, Tcl_Interp *interp, const char *handleId, Ns_DbHandle **handle, - Tcl_HashEntry **hPtrPtr) + Tcl_HashEntry **hPtrPtr) { Tcl_HashEntry *hPtr; int result = TCL_OK; hPtr = Tcl_FindHashEntry(&idataPtr->dbs, handleId); if (hPtr == NULL) { - Ns_TclPrintfResult(interp, "invalid database id: \"%s\"", handleId); - result = TCL_ERROR; + Ns_TclPrintfResult(interp, "invalid database id: \"%s\"", handleId); + result = TCL_ERROR; } else { *handle = (Ns_DbHandle *) Tcl_GetHashValue(hPtr); @@ -1181,7 +1183,7 @@ EnterDbHandle(InterpData *idataPtr, Tcl_Interp *interp, Ns_DbHandle *handle, Tcl { Tcl_HashEntry *hPtr; int isNew, next, len; - char buf[100]; + char buf[100]; NS_NONNULL_ASSERT(idataPtr != NULL); NS_NONNULL_ASSERT(interp != NULL); @@ -1193,7 +1195,7 @@ EnterDbHandle(InterpData *idataPtr, Tcl_Interp *interp, Ns_DbHandle *handle, Tcl len = snprintf(buf, sizeof(buf), "nsdb%x", next++); hPtr = Tcl_CreateHashEntry(&idataPtr->dbs, buf, &isNew); } while (isNew == 0); - + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(buf, len)); Tcl_SetHashValue(hPtr, handle); } diff --git a/nsdb/dbutil.c b/nsdb/dbutil.c index fc17223e..afa8b094 100644 --- a/nsdb/dbutil.c +++ b/nsdb/dbutil.c @@ -269,7 +269,7 @@ Ns_DbInterpretSqlFile(Ns_DbHandle *handle, const char *filename) status = NS_ERROR; break; } - Ns_DStringTrunc(&dsSql, 0); + Ns_DStringSetLength(&dsSql, 0); } else { Ns_DStringNAppend(&dsSql, &c, 1); if (c == '\'') { diff --git a/nslog/nslog.c b/nslog/nslog.c index 98e22666..842fcd68 100644 --- a/nslog/nslog.c +++ b/nslog/nslog.c @@ -84,7 +84,7 @@ static Ns_ReturnCode LogOpen (Log *logPtr); static Ns_ReturnCode LogRoll (Log *logPtr); static Ns_ReturnCode LogClose(Log *logPtr); -static void AppendEscaped(Ns_DString *dsPtr, const char *chars) +static void AppendEscaped(Ns_DString *dsPtr, const char *toProcess) NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2); @@ -163,7 +163,7 @@ Ns_ModuleInit(const char *server, const char *module) Tcl_Obj *dirpath; int rc; - Ns_DStringTrunc(&ds, 0); + Ns_DStringSetLength(&ds, 0); (void) Ns_ModulePath(&ds, server, module, NULL, (char *)0); dirpath = Tcl_NewStringObj(ds.string, -1); Tcl_IncrRefCount(dirpath); @@ -175,7 +175,7 @@ Ns_ModuleInit(const char *server, const char *module) Ns_DStringFree(&ds); return NS_ERROR; } - Ns_DStringTrunc(&ds, 0); + Ns_DStringSetLength(&ds, 0); (void) Ns_ModulePath(&ds, server, module, file, (char *)0); } logPtr->file = Ns_DStringExport(&ds); @@ -405,7 +405,7 @@ LogObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* o if (strstr(ds.string, "suppressquery")) { flags |= LOG_SUPPRESSQUERY; } - Ns_DStringTrunc(&ds, 0); + Ns_DStringSetLength(&ds, 0); Ns_MutexLock(&logPtr->lock); logPtr->flags = flags; Ns_MutexUnlock(&logPtr->lock); @@ -514,40 +514,58 @@ LogObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* o */ static void -AppendEscaped(Ns_DString *dsPtr, const char *chars) +AppendEscaped(Ns_DString *dsPtr, const char *toProcess) { + const char *breakChar; + NS_NONNULL_ASSERT(dsPtr != NULL); - NS_NONNULL_ASSERT(chars != NULL); - - while (likely(*chars != '\0')) { - switch (*chars) { - case '\n': - Ns_DStringNAppend(dsPtr, "\\n", 2); - break; - - case '\r': - Ns_DStringNAppend(dsPtr, "\\r", 2); - break; - - case '\t': - Ns_DStringNAppend(dsPtr, "\\t", 2); - break; - - case '"': - Ns_DStringNAppend(dsPtr, "\\\"", 2); - break; - - case '\\': - Ns_DStringNAppend(dsPtr, "\\\\",2); - break; - - default: - Ns_DStringNAppend(dsPtr, chars, 1); - break; + NS_NONNULL_ASSERT(toProcess != NULL); + + do { + breakChar = strpbrk(toProcess, "\r\n\t\\\""); + if (breakChar == NULL) { + /* + * No break-char found, append all and stop + */ + Ns_DStringNAppend(dsPtr, toProcess, -1); + } else { + /* + * Append the break-char free prefix + */ + Ns_DStringNAppend(dsPtr, toProcess, (int)(breakChar - toProcess)); + + /* + * Escape the break-char + */ + switch (*breakChar) { + case '\n': + Ns_DStringNAppend(dsPtr, "\\n", 2); + break; + case '\r': + Ns_DStringNAppend(dsPtr, "\\r", 2); + break; + case '\t': + Ns_DStringNAppend(dsPtr, "\\t", 2); + break; + case '"': + Ns_DStringNAppend(dsPtr, "\\\"", 2); + break; + case '\\': + Ns_DStringNAppend(dsPtr, "\\\\",2); + break; + default: + /*should not happen */ assert(0); + break; + } + + /* + * Check for further protected characters after the break char. + */ + toProcess = breakChar + 1; } - ++chars; - } + } while (breakChar != NULL); } + /* *---------------------------------------------------------------------- @@ -757,7 +775,7 @@ LogTrace(void *arg, Ns_Conn *conn) */ memcpy(buffer, logPtr->buffer.string, bufferSize); bufferPtr = buffer; - Ns_DStringTrunc(&logPtr->buffer, 0); + Ns_DStringSetLength(&logPtr->buffer, 0); status = NS_OK; } else { status = LogFlush(logPtr, &logPtr->buffer); @@ -882,7 +900,7 @@ LogFlush(Log *logPtr, Ns_DString *dsPtr) ns_close(logPtr->fd); logPtr->fd = NS_INVALID_FD; } - Ns_DStringTrunc(dsPtr, 0); + Ns_DStringSetLength(dsPtr, 0); } return (logPtr->fd == NS_INVALID_FD) ? NS_ERROR : NS_OK; diff --git a/nsperm/doc/mann/ns_perm.man b/nsperm/doc/mann/ns_perm.man index 8dc0eb57..5f398027 100644 --- a/nsperm/doc/mann/ns_perm.man +++ b/nsperm/doc/mann/ns_perm.man @@ -13,7 +13,7 @@ and the specified user text (userfield) into the users database. [list_begin definitions] -[call [cmd ns_perm] [arg adduser] [opt [arg -allow]] [opt [arg -deny]] [opt [arg -clear]] [opt [arg {-salt s}]] [arg name] [arg pass] [arg userfield] [opt host...]] +[call [cmd "ns_perm adduser"] [opt [arg -allow]] [opt [arg -deny]] [opt [arg -clear]] [opt [arg {-salt s}]] [arg name] [arg pass] [arg userfield] [opt host...]] [arg -allow] and hostnames are specified, the user will be allowed on the specified hostnames. @@ -37,85 +37,89 @@ by the ns_perm command with specified salt. [arg -clear] Tells that we keep password in clear text -[call [cmd ns_perm] [arg deluser] [arg name]] +[call [cmd "ns_perm deluser"] [arg name]] ns_perm deluser deletes user from the memory -[call [cmd ns_perm] [arg addgroup] [arg name] [arg user] [opt [arg user...]]] +[call [cmd "ns_perm addgroup"] [arg name] [arg user] [opt [arg user...]]] ns_perm addgroup creates a new group with the specified name that includes the users listed after the name. -[call [cmd ns_perm] [arg delgroup] [arg name]] +[call [cmd "ns_perm delgroup"] [arg name]] ns_perm delgroup deletes group from the memory -[call [cmd ns_perm] [arg allowuser] [opt -noinherit]] [arg method] [arg url] [arg user...]]] +[call [cmd "ns_perm allowuser"] [opt -noinherit]] [arg method] [arg url] [arg user...]]] ns_perm allowuser allows the specified user access to the specified method and URL combination. If -noinherit is specified, only access to the exact URL is allowed; otherwise, URLs under that URL are allowed as well. -[call [cmd ns_perm] [arg denyuser] [opt [arg -noinherit]] [arg method] [arg url] [arg user...]] +[call [cmd "ns_perm denyuser"] [opt [arg -noinherit]] [arg method] [arg url] [arg user...]] ns_perm denyuser denies the specified user access to the specified method and URL combination. If -noinherit is specified, only access to the exact URL is denied; otherwise, URLs under that URL are denied as well. -[call [cmd ns_perm] [arg allowgroup] [opt [arg -noinherit]] [arg method] [arg url] [arg group...]] +[call [cmd "ns_perm allowgroup"] [opt [arg -noinherit]] [arg method] [arg url] [arg group...]] ns_perm allowgroup allows the specified group access to the specified method and URL combination. If -noinherit is specified, only access to the exact URL is allowed; otherwise, URLs under that URL are allowed as well. -[call [cmd ns_perm] [arg denygroup] [opt [arg -noinherit]] [arg method] [arg url] [arg group...]] +[call [cmd "ns_perm denygroup"] [opt [arg -noinherit]] [arg method] [arg url] [arg group...]] ns_perm denygroup denies the specified group access to the specified method and URL combination. If -noinherit is specified, only access to the exact URL is denied; otherwise, URLs under that URL are denied as well. -[call [cmd ns_perm] [arg checkpass] [arg user] [arg passwd]] +[call [cmd "ns_perm checkpass"] [arg user] [arg passwd]] ns_perm checkpass checks that the specified plain-text password is correct for the specified user. A Tcl error is thrown if it does not match. -[call [cmd ns_perm] [arg setpass] [arg user] [arg encpass]] +[call [cmd "ns_perm setpass"] [arg user] [arg encpass]] ns_perm setpass updates the specified user's password to the encrypted password encpass. The password should be encrypted using ns_encrypt. -[call [cmd ns_perm] [arg listusers]] +[call [cmd "ns_perm listusers"]] Produce Tcl list of all current users in the format username password .... -[call [cmd ns_perm] [arg listgroups]] +[call [cmd "ns_perm listgroups"]] Produce Tcl list with all registered groups in the format: groupname {user ...} ... -[call [cmd ns_perm] [arg listperms]] +[call [cmd "ns_perm listperms"]] Produce Tcl list with all registered allow/deny statements for each url [call [cmd ns_permreload]] -Reloads all ns_perm files, on very busy sites there could happen authentication denies because -this command clears the memory first and then loads files from the disk +Reloads all ns_perm files, on very busy sites there could happen +authentication denies because this command clears the memory first and +then loads files from the disk [list_end] + [section CONFIGURATION] -The following configuration options are available to control permission module +The following configuration options are available to control +permission module: [list_begin definitions] [def htaccess] -This parameter if set to true, enables .htaccess mode, similar to what the Apache web server -has but very simple and limited in functionality. +This parameter if set to true, enables .htaccess mode, similar to what +the Apache web server has but very simple and limited in +functionality. -[para] -On every request the server looks for .htaccess file in the current request directory and loads it if -modified since the last read. The structure of the file is simple: +[para] On every request the server looks for .htaccess file in the +current request directory and loads it if modified since the last +read. The structure of the file is simple: [example_begin] allow user ... @@ -124,14 +128,16 @@ deny user ... [def passwdfile] -This parameter determines which file with users and passwords needs to be checked for modification -and reloaded automatically. +This parameter determines in .htaccess mode which file with users and +passwords needs to be checked for modification and reloaded +automatically. If .htaccess mode is not active, the parameter is +ignored. [list_end] [example_begin] ns_section "ns/server/servername/module/nsperm" -ns_param htaccess false +ns_param htaccess true ns_param passwdfile /usr/local/ns/modules/nsperm/passwd [example_end] diff --git a/nsperm/nsperm.c b/nsperm/nsperm.c index 71bed1f7..9ea9c550 100644 --- a/nsperm/nsperm.c +++ b/nsperm/nsperm.c @@ -1651,7 +1651,7 @@ static int CreateNonce(const char *privatekey, char **nonce, const char *uri) Ns_HexString(sig, buf, 16, NS_TRUE); /* encode the current time and MD5 string into the nonce */ - Ns_DStringTrunc(&ds, 0); + Ns_DStringSetLength(&ds, 0); Ns_DStringPrintf(&ds, "%" PRIu64 " %s", (int64_t) now, buf); Ns_HtuuEncode((unsigned char *) ds.string, (unsigned int) ds.length, bufcoded); diff --git a/nsproxy/nsproxylib.c b/nsproxy/nsproxylib.c index fcf64343..de11212d 100644 --- a/nsproxy/nsproxylib.c +++ b/nsproxy/nsproxylib.c @@ -369,7 +369,7 @@ static Ns_DString defexec; /* Stores full path of the proxy executab * * Nsproxy_Init -- * - * libnsproxy initialisation. + * libnsproxy initialization. * * Results: * None. @@ -618,8 +618,8 @@ Ns_ProxyMain(int argc, char **argv, Tcl_AppInitProc *init) if (SendBuf(&proc, -1, &out) == NS_FALSE) { break; } - Tcl_DStringTrunc(&in, 0); - Tcl_DStringTrunc(&out, 0); + Tcl_DStringSetLength(&in, 0); + Tcl_DStringSetLength(&out, 0); } if (uarg != NULL) { @@ -1061,7 +1061,7 @@ Send(Tcl_Interp *interp, Proxy *proxyPtr, const char *script) req.len = htonl((uint32_t)len); req.major = htons(MAJOR_VERSION); req.minor = htons(MINOR_VERSION); - Tcl_DStringTrunc(&proxyPtr->in, 0); + Tcl_DStringSetLength(&proxyPtr->in, 0); Tcl_DStringAppend(&proxyPtr->in, (char *) &req, sizeof(req)); if (len > 0u) { Tcl_DStringAppend(&proxyPtr->in, script, (int)len); @@ -1185,7 +1185,7 @@ Recv(Tcl_Interp *interp, Proxy *proxyPtr, int *resultPtr) } else if (proxyPtr->state == Busy) { err = ENoWait; } else { - Tcl_DStringTrunc(&proxyPtr->out, 0); + Tcl_DStringSetLength(&proxyPtr->out, 0); if (RecvBuf(proxyPtr->slavePtr, proxyPtr->conf.trecv, &proxyPtr->out) == NS_FALSE) { err = ERecv; @@ -1615,7 +1615,7 @@ ProxyObjCmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* objv) Tcl_HashSearch search; Tcl_Obj *listObj; - static const char *const opts[] = { + static const char *opts[] = { "get", "put", "release", "eval", "cleanup", "configure", "ping", "free", "active", "handles", "clear", "stop", "send", "wait", "recv", "pools", NULL @@ -1898,7 +1898,7 @@ ConfigureObjCmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CONST* o Proxy *proxyPtr; int flag, n, result = TCL_OK, reap = 0; - static const char *const flags[] = { + static const char *flags[] = { "-init", "-reinit", "-maxslaves", "-exec", "-env", "-gettimeout", "-evaltimeout", "-sendtimeout", "-recvtimeout", "-waittimeout", "-idletimeout", "-maxruns", NULL @@ -2730,8 +2730,8 @@ ResetProxy(Proxy *proxyPtr) } Ns_MutexUnlock(&poolPtr->lock); - Tcl_DStringTrunc(&proxyPtr->in, 0); - Tcl_DStringTrunc(&proxyPtr->out, 0); + Tcl_DStringSetLength(&proxyPtr->in, 0); + Tcl_DStringSetLength(&proxyPtr->out, 0); } @@ -2983,15 +2983,41 @@ ReaperThread(void *UNUSED(arg)) case SIGKILL: slavePtr->signal = -1; break; } } - if (slavePtr->signal == -1 || WaitFd(slavePtr->rfd, POLLIN, 0)) { - + + if (slavePtr->signal == -1 + || slavePtr->rfd == NS_INVALID_FD + || WaitFd(slavePtr->rfd, POLLIN, 0)) { + /* - * We either have a zombie or the process has exited ok - * so splice it out the list. + * We either have timeouted eval (rfd==NS_INVALID_FD), a + * zombie or the process has exited ok so splice it out the + * list. */ - if (slavePtr->signal >= 0) { - (void) Ns_WaitProcess(slavePtr->pid); /* Should not really wait */ + int waitStatus = 0; + + /* + * Pass waitStatus ptr to Ns_WaitForProcessStatus() to + * indicate that we want to handle the signal here and to + * suppress warning entries in the error.log. + * + * The following wait operation should not really wait. + */ + (void) Ns_WaitForProcessStatus(slavePtr->pid, NULL, &waitStatus); +#ifdef WTERMSIG + if (slavePtr->signal != 0 && WTERMSIG(waitStatus) != 0) { + Ns_LogSeverity severity; + + if (WTERMSIG(waitStatus) != slavePtr->signal) { + severity = Warning; + } else { + severity = Notice; + } + Ns_Log(severity, "nsproxy process %d killed with signal %d (%s)", + slavePtr->pid, + WTERMSIG(waitStatus), strsignal(WTERMSIG(waitStatus))); + } +#endif } else { Ns_Log(Warning, "nsproxy: zombie: %ld", (long)slavePtr->pid); } @@ -3254,8 +3280,11 @@ ReleaseProxy(Tcl_Interp *interp, Proxy *proxyPtr) result = Eval(interp, proxyPtr, Tcl_DStringValue(&ds), -1); } Tcl_DStringFree(&ds); - } else if (proxyPtr->state == Busy) { + + } else if ( (proxyPtr->state == Busy) && (proxyPtr->slavePtr != NULL) ) { + proxyPtr->slavePtr->signal = 0; Ns_Log(Notice, "releasing busy proxy %s", proxyPtr->id); + /* * In case the proxy is busy, make sure to drain the pipe, otherwise * the proxy might be hanging in a send operation. Closing our end diff --git a/nsproxy/nsproxymod.c b/nsproxy/nsproxymod.c index 8a1899ae..7248fff7 100644 --- a/nsproxy/nsproxymod.c +++ b/nsproxy/nsproxymod.c @@ -81,7 +81,7 @@ Nsproxy_Init(Tcl_Interp *interp) * * Ns_ModuleInit -- * - * NaviServer module initialisation routine. + * NaviServer module initialization routine. * * Results: * NS_OK/NS_ERROR diff --git a/nssock/doc/mann/nssock.man b/nssock/doc/mann/nssock.man index 874cad42..549675a0 100644 --- a/nssock/doc/mann/nssock.man +++ b/nssock/doc/mann/nssock.man @@ -182,9 +182,9 @@ Number of writer threads. (integer, default: 0) [section EXAMPLES] -In case, one requires for one NaviServer to listen on several ports, -it is possible to load the nssock module twice with different names, -here [term nssock1] and [term nssock2]. +In case, one requires for one server (here [term server1]) to listen +on several ports, it is possible to load the nssock driver twice with +different names, here [term nssock1] and [term nssock2]. [example_begin] ns_section ns/server/server1/modules @@ -198,6 +198,66 @@ here [term nssock1] and [term nssock2]. ns_param port 8001 [example_end] +The second example shows how to define two web servers "s1" and "s2", +which receive requests from three virtual servers "foo.com", "bar.com" +and "baz.com". Using virtual servers is a common technique, where +e.g. for the same IP address, multiple DNS names are +registered. According to HTTP/1.1, clients send the host name in the +host header field to the server, which can behave differently +depending on contents of this field. This is called a "virtual web +server". + +[para] + +Assume for the IP address of the server the DNS names [term foo.com], +[term bar.com] and [term baz.com] are registered. We define server +"s1" and "s2" such that "s1" should receive requests from +[term foo.com], and "s2" should receive requests from [term bar.com] +and [term baz.com]. Servers "s1" and "s2" have different +[term pagedir] definitions. For requests with missing/invalid host +header fields, we have to define a [term defaultserver] to handle such +requests. + +[para] + +In the section [term ns/module/nssock/servers] we define the +mapping between the host names and the defined servers. Note, that one +can define multiple DNS names also for a single server. + +[example_begin] + # + # Define two servers + # + ns_section ns/servers + ns_param s1 "Virtual Server s1" + ns_param s2 "Virtual Server s2 " + + ns_section ns/server/s1/fastpath + ns_param pagedir /var/www/s1 + + ns_section ns/server/s2/fastpath + ns_param pagedir /var/www/s2 + + # + # Define nssock driver, directing requests to the virtual servers + # + ns_section ns/modules + ns_param nssock nssock.so + + ns_section ns/module/nssock + ns_param port 8000 + ns_param defaultserver s1 + + # + # Define the mapping between the DNS names and the servers. + # + ns_section ns/module/nssock/servers + ns_param s1 foo.com + ns_param s2 bar.com + ns_param s2 baz.com +[example_end] + + More to come here... diff --git a/nsssl/doc/mann/nsssl.man b/nsssl/doc/mann/nsssl.man index dc42c296..bf29bf6f 100644 --- a/nsssl/doc/mann/nsssl.man +++ b/nsssl/doc/mann/nsssl.man @@ -58,13 +58,13 @@ HTTP Strict Transport Security (HSTS) is enabled. [section EXAMPLES] The module is typically loaded per server (specified -below in the variable "servername"): +below in the variable "server"): [example_begin] - ns_section ns/server/${servername}/modules + ns_section ns/server/${server}/modules ns_param nsssl nsssl.so - ns_section ns/server/${servername}/module/nsssl + ns_section ns/server/${server}/module/nsssl ns_param certificate /usr/local/ns/modules/nsssl/server.pem ns_param address 0.0.0.0 ns_param port 443 diff --git a/nsssl/nsssl.c b/nsssl/nsssl.c index 58014da6..b381670e 100644 --- a/nsssl/nsssl.c +++ b/nsssl/nsssl.c @@ -197,7 +197,7 @@ Ns_ModuleInit(const char *server, const char *module) for (n = 0; n < num; n++) { Ns_DStringPrintf(&ds, "nsssl:%d", n); Ns_MutexSetName(driver_locks + n, ds.string); - Ns_DStringTrunc(&ds, 0); + Ns_DStringSetLength(&ds, 0); } } #if OPENSSL_VERSION_NUMBER < 0x10100000L diff --git a/nsthread/memory.c b/nsthread/memory.c index 3bacbd28..c9896e8a 100644 --- a/nsthread/memory.c +++ b/nsthread/memory.c @@ -129,6 +129,94 @@ ns_strdup(const char *old) return p; } + +/* + *---------------------------------------------------------------------- + * + * ns_uint32toa, ns_uint64toa -- + * + * This procedure formats an uint32_t or uint62_t into a sequence of + * decimal digits in a buffer. It is the caller's responsibility to + * ensure that enough storage is available. This procedure has the effect + * of sprintf(buffer, "%...d", n) but is substantially faster + * + * Results: + * Length of the written digits, not including the terminating "\0". + * + * Side effects: + * The formatted characters are written into the storage pointer to by + * the "buffer" argument. + * + *---------------------------------------------------------------------- + */ + +int +ns_uint32toa( + char *buffer, /* Points to the storage into which the + * formatted characters are written. */ + uint32_t n) /* The value to be converted. */ +{ + char temp[TCL_INTEGER_SPACE]; + register char *p = temp; + int len = 0; + + /* + * Compute the digits. + */ + do { + *p++ = (char)((n % 10u) + UCHAR('0')); + n /= 10u; + len ++; + } while (likely(n > 0u)); + + /* + * Reverse the digits. + */ + do { + *buffer++ = *--p; + } while (likely(p != temp)); + + /* + * Terminate the string + */ + *buffer = '\0'; + + return len; +} + +int +ns_uint64toa( + char *buffer, /* Points to the storage into which the + * formatted characters are written. */ + uint64_t n) /* The value to be converted. */ +{ + char temp[TCL_INTEGER_SPACE]; + register char *p = temp; + int len = 0; + + /* + * Compute the digits. + */ + do { + *p++ = (char)((n % 10u) + UCHAR('0')); + n /= 10u; + len ++; + } while (likely(n > 0u)); + + /* + * Reverse the digits. + */ + do { + *buffer++ = *--p; + } while (likely(p != temp)); + + /* + * Terminate the string + */ + *buffer = '\0'; + + return len; +} /* * Local Variables: * mode: c diff --git a/nsthread/mutex.c b/nsthread/mutex.c index fcbf155f..c2c62223 100644 --- a/nsthread/mutex.c +++ b/nsthread/mutex.c @@ -78,7 +78,7 @@ typedef struct Mutex { } Mutex; #define GETMUTEX(mutex) (*(mutex) != NULL ? ((Mutex *)*(mutex)) : GetMutex((mutex))) -static Mutex *GetMutex(Ns_Mutex *mutex) NS_GNUC_NONNULL(1); +static Mutex *GetMutex(Ns_Mutex *mutex) NS_GNUC_NONNULL(1) NS_GNUC_RETURNS_NONNULL; static Mutex *firstMutexPtr; @@ -115,7 +115,9 @@ Ns_MutexInit(Ns_Mutex *mutex) firstMutexPtr = mutexPtr; mutexPtr->id = nextid++; - snprintf(mutexPtr->name, sizeof(mutexPtr->name), "mu%" PRIuPTR, mutexPtr->id); + mutexPtr->name[0] = 'm'; + mutexPtr->name[1] = 'u'; + (void) ns_uint64toa(&mutexPtr->name[2], (uint64_t)mutexPtr->id); Ns_MasterUnlock(); //fprintf(stderr, "=== created mutex %ld name %s\n", mutexPtr->id, mutexPtr->name); @@ -158,7 +160,6 @@ Ns_MutexSetName2(Ns_Mutex *mutex, const char *prefix, const char *name) NS_NONNULL_ASSERT(mutex != NULL); NS_NONNULL_ASSERT(prefix != NULL); - mutexPtr = GETMUTEX(mutex); prefixLength = strlen(prefix); if (prefixLength > NS_THREAD_NAMESIZE - 1) { prefixLength = NS_THREAD_NAMESIZE - 1; @@ -168,8 +169,13 @@ Ns_MutexSetName2(Ns_Mutex *mutex, const char *prefix, const char *name) if ((nameLength + prefixLength + 1) > NS_THREAD_NAMESIZE) { nameLength = NS_THREAD_NAMESIZE - prefixLength - 1; } + } else { + nameLength = 0u; } + mutexPtr = GETMUTEX(mutex); + assert(mutexPtr != NULL); + Ns_MasterLock(); p = mutexPtr->name; memcpy(p, prefix, prefixLength + 1u); @@ -253,6 +259,7 @@ Ns_MutexLock(Ns_Mutex *mutex) NS_NONNULL_ASSERT(mutex != NULL); mutexPtr = GETMUTEX(mutex); + assert(mutexPtr != NULL); if (unlikely(!NsLockTry(mutexPtr->lock))) { NsLockSet(mutexPtr->lock); ++mutexPtr->nbusy; @@ -463,6 +470,8 @@ NsGetLock(Ns_Mutex *mutex) NS_NONNULL_ASSERT(mutex != NULL); mutexPtr = GETMUTEX(mutex); + assert(mutexPtr != NULL); + return mutexPtr->lock; } @@ -472,7 +481,7 @@ NsGetLock(Ns_Mutex *mutex) * * GetMutex -- * - * Cast an Ns_Mutex to a Mutex, initializing if needed. + * Cast a Ns_Mutex to a Mutex, initializing if needed. * * Results: * Pointer to Mutex. diff --git a/nsthread/nsthreadtest.c b/nsthread/nsthreadtest.c index 19948f3a..c358f8d7 100644 --- a/nsthread/nsthreadtest.c +++ b/nsthread/nsthreadtest.c @@ -322,7 +322,7 @@ DumpString(Tcl_DString *dsPtr) } Tcl_Free((char *) largv); } - Tcl_DStringTrunc(dsPtr, 0); + Tcl_DStringSetLength(dsPtr, 0); } diff --git a/nsthread/pthread.c b/nsthread/pthread.c index b2d4d8b2..1573c3ae 100644 --- a/nsthread/pthread.c +++ b/nsthread/pthread.c @@ -72,7 +72,7 @@ static pthread_key_t key; * * Nsthreads_LibInit -- * - * Pthread library initialisation routine. + * Pthread library initialization routine. * * Results: * None. @@ -469,12 +469,11 @@ NsThreadExit(void *arg) void Ns_ThreadJoin(Ns_Thread *thread, void **argPtr) { - pthread_t thr = (pthread_t) *thread; int err; NS_NONNULL_ASSERT(thread != NULL); - err = pthread_join(thr, argPtr); + err = pthread_join((pthread_t)*thread, argPtr); if (err != 0) { NsThreadFatal("Ns_ThreadJoin", "pthread_join", err); } diff --git a/nsthread/reentrant.c b/nsthread/reentrant.c index c9c53cf2..361f0fcf 100644 --- a/nsthread/reentrant.c +++ b/nsthread/reentrant.c @@ -125,7 +125,7 @@ ns_inet_ntoa(struct sockaddr *saPtr) Tls *tlsPtr = GetTls(); union { unsigned int i; - unsigned char b[4]; + unsigned char b[4]; } addr4; NS_NONNULL_ASSERT(saPtr != NULL); @@ -359,14 +359,12 @@ ns_strtok(char *str, const char *sep) Tls *tlsPtr = GetTls(); - NS_NONNULL_ASSERT(str != NULL); NS_NONNULL_ASSERT(sep != NULL); return strtok_s(str, sep, &tlsPtr->stbuf); #elif defined(_WIN32) - NS_NONNULL_ASSERT(str != NULL); NS_NONNULL_ASSERT(sep != NULL); return strtok(str, sep); @@ -374,7 +372,6 @@ ns_strtok(char *str, const char *sep) Tls *tlsPtr = GetTls(); - NS_NONNULL_ASSERT(str != NULL); NS_NONNULL_ASSERT(sep != NULL); return strtok_r(str, sep, &tlsPtr->stbuf); diff --git a/nsthread/rwlock.c b/nsthread/rwlock.c index dc7577ed..38bc682c 100644 --- a/nsthread/rwlock.c +++ b/nsthread/rwlock.c @@ -64,7 +64,7 @@ typedef struct RwLock { } RwLock; static RwLock *GetRwLock(Ns_RWLock *rwPtr) - NS_GNUC_NONNULL(1); + NS_GNUC_NONNULL(1) NS_GNUC_RETURNS_NONNULL; /* diff --git a/nsthread/thread.c b/nsthread/thread.c index ec158ae2..700095fb 100644 --- a/nsthread/thread.c +++ b/nsthread/thread.c @@ -375,7 +375,9 @@ Ns_ThreadList(Tcl_DString *dsPtr, Ns_ThreadArgProc *proc) written = snprintf(buf, sizeof(buf), " %p", thrPtr->arg); Tcl_DStringAppend(dsPtr, buf, written); } - written = snprintf(buf, sizeof(buf), " %lu", (unsigned long) thrPtr->ostid); + + Tcl_DStringAppend(dsPtr, " ", 1); + written = ns_uint32toa(buf, (uint32_t)thrPtr->ostid); Tcl_DStringAppend(dsPtr, buf, written); Tcl_DStringEndSublist(dsPtr); diff --git a/nsthread/tls.c b/nsthread/tls.c index bbfa333e..3b4bbf41 100644 --- a/nsthread/tls.c +++ b/nsthread/tls.c @@ -106,19 +106,20 @@ Ns_TlsAlloc(Ns_Tls *keyPtr, Ns_TlsCleanup *cleanup) void Ns_TlsSet(Ns_Tls *keyPtr, void *value) { - void **slots = NsGetTls(); uintptr_t key; NS_NONNULL_ASSERT(keyPtr != NULL); key = (uintptr_t) *keyPtr; - if (key < 1 || key >= NS_THREAD_MAXTLS) { Tcl_Panic("Ns_TlsSet: invalid key: %" PRIuPTR ": should be between 1 and %" PRIuPTR, key, nsThreadMaxTls); + } else { + void **slots = NsGetTls(); + + slots[key] = value; } - slots[key] = value; } @@ -141,18 +142,23 @@ Ns_TlsSet(Ns_Tls *keyPtr, void *value) void * Ns_TlsGet(Ns_Tls *keyPtr) { - void **slots = NsGetTls(); - uintptr_t key; + uintptr_t key; + void *result; NS_NONNULL_ASSERT(keyPtr != NULL); key = (uintptr_t) *keyPtr; if (key < 1 || key >= NS_THREAD_MAXTLS) { + result = NULL; Tcl_Panic("Ns_TlsGet: invalid key: %" PRIuPTR ": should be between 1 and %" PRIuPTR, key, nsThreadMaxTls); + } else { + void **slots = NsGetTls(); + + result = slots[key]; } - return slots[key]; + return result; } diff --git a/nsthread/winthread.c b/nsthread/winthread.c index eb8d30e0..5f79311d 100644 --- a/nsthread/winthread.c +++ b/nsthread/winthread.c @@ -104,7 +104,7 @@ static DWORD tlskey; * * Nsthreads_LibInit -- * - * Pthread library initialisation routine. + * Pthread library initialization routine. * * Results: * None. diff --git a/openacs-config.tcl b/openacs-config.tcl index 29eed208..d33516d9 100644 --- a/openacs-config.tcl +++ b/openacs-config.tcl @@ -11,7 +11,7 @@ ns_log notice "nsd.tcl: starting to read config file..." #--------------------------------------------------------------------- # change to 80 and 443 for production use set httpport 8000 -set httpsport 8443 +#set httpsport 8443 # The hostname and address should be set to actual values. # setting the address to 0.0.0.0 means AOLserver listens on all interfaces @@ -314,12 +314,13 @@ ns_section "ns/server/${server}/fastpath" # #--------------------------------------------------------------------- -# OpenACS specfic settings (per server) +# OpenACS specific settings (per server) #--------------------------------------------------------------------- # # Define/override kernel parameters in section /acs # ns_section ns/server/${server}/acs + ns_param NsShutdownWithNonZeroExitCode 1 # ns_param LogIncludeUserId 1 # # Define/override OpenACS package parameters in section /acs/PACKAGENAME @@ -386,7 +387,7 @@ foreach address $addresses suffix $suffixes { # ns_param writerbufsize 8192 ;# 8192, buffer size for writer threads # ns_param writerstreaming true ;# false; activate writer for streaming HTML output (when using ns_write) # ns_param driverthreads 2 ;# 1; use multiple driver threads (requires support of SO_REUSEPORT) - ns_param extraheaders $extraheaders + ns_param extraheaders $nssock_extraheaders } @@ -409,7 +410,7 @@ ns_section ns/server/${server}/module/nslog # ns_param logcombined true ;# true, Log in NSCA Combined Log Format (referer, user-agent) ns_param checkforproxy $proxy_mode ;# false, check for proxy header (X-Forwarded-For) # - # Add extra entries to the access log via specfiying a comma delimited + # Add extra entries to the access log via specifying a comma delimited # list of request header fields in "extendedheaders" # if {[ns_config "ns/server/${server}/acs" LogIncludeUserId 0]} { @@ -452,25 +453,27 @@ ns_section ns/server/${server}/module/nspam ns_param PamDomain "pam_domain" -#--------------------------------------------------------------------- -# SSL/TLS -#--------------------------------------------------------------------- -foreach address $addresses suffix $suffixes { - ns_section "ns/server/${server}/module/nsssl_$suffix" - ns_param address $address - ns_param port $httpsport - ns_param hostname $hostname - ns_param ciphers "ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!RC4" - ns_param protocols "!SSLv2" - ns_param certificate $serverroot/etc/certfile.pem - ns_param verify 0 - ns_param writerthreads 2 - ns_param writersize 1024 - ns_param writerbufsize 16384 ;# 8192, buffer size for writer threads - #ns_param writerstreaming true ;# false - #ns_param deferaccept true ;# false, Performance optimization - ns_param maxinput [expr {$max_file_upload_mb * 1024*1024}] ;# Maximum File Size for uploads in bytes - ns_param extraheaders $nsssl_extraheaders +if {[info exists httpsport]} { + #--------------------------------------------------------------------- + # SSL/TLS + #--------------------------------------------------------------------- + foreach address $addresses suffix $suffixes { + ns_section "ns/server/${server}/module/nsssl_$suffix" + ns_param address $address + ns_param port $httpsport + ns_param hostname $hostname + ns_param ciphers "ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!RC4" + ns_param protocols "!SSLv2" + ns_param certificate $serverroot/etc/certfile.pem + ns_param verify 0 + ns_param writerthreads 2 + ns_param writersize 1024 + ns_param writerbufsize 16384 ;# 8192, buffer size for writer threads + #ns_param writerstreaming true ;# false + #ns_param deferaccept true ;# false, Performance optimization + ns_param maxinput [expr {$max_file_upload_mb * 1024*1024}] ;# Maximum File Size for uploads in bytes + ns_param extraheaders $nsssl_extraheaders + } } #--------------------------------------------------------------------- @@ -587,8 +590,8 @@ ns_section ns/server/${server}/modules ns_param nsproxy ${bindir}/nsproxy.so if {[info exists address_v4]} { ns_param nssock_v4 ${bindir}/nssock.so } if {[info exists address_v6]} { ns_param nssock_v6 ${bindir}/nssock.so } - #if {[info exists address_v4]} { ns_param nsssl_v4 ${bindir}/nsssl.so } - #if {[info exists address_v6]} { ns_param nsssl_v6 ${bindir}/nsssl.so } + if {[info exists address_v4] && [info exists httpsport]} { ns_param nsssl_v4 ${bindir}/nsssl.so } + if {[info exists address_v6] && [info exists httpsport]} { ns_param nsssl_v6 ${bindir}/nsssl.so } # # Determine, if libthread is installed diff --git a/sample-config.tcl.in b/sample-config.tcl.in index 5e62649c..52670463 100644 --- a/sample-config.tcl.in +++ b/sample-config.tcl.in @@ -114,6 +114,11 @@ ns_section "ns/parameters" # Pid file of the server process ns_param pidfile ${logdir}/nsd-${servername}.pid + # Timeout for shutdown in seconds to let existing connections and + # background jobs finish. When this time limit is exceeded the server + # shuts down immediately. + ns_param shutdowntimeout 20 + # Min size of the uploaded file to enable progress tracking ns_param progressminsize 0 diff --git a/tcl/aolserver-openacs.tcl b/tcl/aolserver-openacs.tcl index 68d542f2..df221be1 100644 --- a/tcl/aolserver-openacs.tcl +++ b/tcl/aolserver-openacs.tcl @@ -45,7 +45,7 @@ if {1} { package require XOTcl 2 package require nx::serializer namespace import -force ::xotcl::* - ns_log notice "XOTcl [package require XOTcl 2] loaded" + ns_log notice "XOTcl [package require XOTcl 2] loaded featuring: [array get ::nsf::config]" }]} { # We could not load XOTcl 2; fall back and try to load XOTcl 1 set xotcl 1 diff --git a/tcl/nstrace.tcl b/tcl/nstrace.tcl index 295b8f46..a9b00ab3 100644 --- a/tcl/nstrace.tcl +++ b/tcl/nstrace.tcl @@ -432,7 +432,7 @@ ns_runonce { # Serialize XOTcl/NX content # if {[catch {::Serializer all} objects]} { - ns_log notice "NX/XOTcl extension not loaded; will not copy objects\ + ns_log warning "NX/XOTcl extension not loaded; classes an objects will not be included in blueprint\ (error: $objects; $::errorInfo)." set objects "" } else { diff --git a/tcl/sendmail.tcl b/tcl/sendmail.tcl index 746bcee3..219b9730 100644 --- a/tcl/sendmail.tcl +++ b/tcl/sendmail.tcl @@ -51,7 +51,7 @@ proc ns_sendmail args { lassign $args to from subject body headers bcc cc if {![string match -* $to]} { - ns_log warning "Deprecated syntax. Use: [list ns_sendmail -to $to -from $from -subject $subject -body $body -headers $headers -bcc $bcc -cc $cc]" + ns_log warning "Deprecated syntax. Use: [list ns_sendmail -to $to -from $from -subject $subject -body BODY -headers HEADERS -bcc $bcc -cc $cc]" } else { ns_parseargs { {-to ""} @@ -182,7 +182,7 @@ proc ns_sendmail args { # Put message essentials # - set date [ns_httptime [clock seconds]] + set date [clock format [clock seconds] -format "%a, %d %b %Y %T %z"] set rfcto [join $tolist ", "] append msg "To: " $rfcto \n diff --git a/tests/cookies.test b/tests/cookies.test index d47b65d8..5ee26087 100644 --- a/tests/cookies.test +++ b/tests/cookies.test @@ -9,7 +9,7 @@ namespace import -force ::tcltest::* test cookie-1.1 {basic syntax} -body { ns_getcookie -} -returnCodes error -result {wrong # args: should be "ns_getcookie ?-include_set_cookies include_set_cookies? ?--? name ?default?"} +} -returnCodes error -result {wrong # args: should be "ns_getcookie ?-all all? ?-include_set_cookies include_set_cookies? ?--? name ?default?"} test cookie-1.2 {basic syntax} -body { ns_setcookie @@ -244,7 +244,7 @@ test cookie-4.5 {get missing cookie} -setup { nstest::http -getbody 1 GET /cookie-4.5 } -cleanup { ns_unregister_op GET /cookie-4.5 -} -result {200 {no matching cookie}} +} -result {200 {no such cookie}} test cookie-4.6 {get cookie with cookie-set values} -setup { @@ -269,9 +269,60 @@ test cookie-4.7 {get missing cookie} -setup { nstest::http -getbody 1 -getheaders {Set-Cookie} GET /cookie-4.7 } -cleanup { ns_unregister_op GET /cookie-4.7 -} -result {200 {foo="foovalue"; HttpOnly} {no matching cookie}} +} -result {200 {foo="foovalue"; HttpOnly} {no such cookie}} +test cookie-4.8 {get all named cookies, single cookie} -setup { + ns_register_proc GET /cookie-4.8 { + ns_return 200 text/plain [ns_getcookie -all t x] + } +} -body { + nstest::http -setheaders {Cookie x=\"hello\"} -getbody 1 GET /cookie-4.8 +} -cleanup { + ns_unregister_op GET /cookie-4.8 +} -result {200 hello} + + +test cookie-4.9a {get all named cookies, multiple cookies} -setup { + ns_register_proc GET /cookie-4.9 { + ns_return 200 text/plain [ns_getcookie -all t x] + } +} -body { + nstest::http -setheaders {Cookie {x="hello"; x="world"}} -getbody 1 GET /cookie-4.9 +} -cleanup { + ns_unregister_op GET /cookie-4.9 +} -result {200 {hello world}} + +test cookie-4.9b {get all named cookies, multiple cookies, skip} -setup { + ns_register_proc GET /cookie-4.9 { + ns_return 200 text/plain [ns_getcookie -all t x] + } +} -body { + nstest::http -setheaders {Cookie {x=hello; y="njet"; x=world}} -getbody 1 GET /cookie-4.9 +} -cleanup { + ns_unregister_op GET /cookie-4.9 +} -result {200 {hello world}} + +test cookie-4.9c {get all named cookies, multiple cookies, no spaces, terminator} -setup { + ns_register_proc GET /cookie-4.9 { + ns_return 200 text/plain [ns_getcookie -all t x] + } +} -body { + nstest::http -setheaders {Cookie {x=hello;y=njet;x=world;}} -getbody 1 GET /cookie-4.9 +} -cleanup { + ns_unregister_op GET /cookie-4.9 +} -result {200 {hello world}} + +test cookie-4.9d {get all named cookies, multiple cookies, no spaces, terminator, other cookie at the end} -setup { + ns_register_proc GET /cookie-4.9 { + ns_return 200 text/plain [ns_getcookie -all t x] + } +} -body { + nstest::http -setheaders {Cookie {x=hello;y=njet;x=world;z=1}} -getbody 1 GET /cookie-4.9 +} -cleanup { + ns_unregister_op GET /cookie-4.9 +} -result {200 {hello world}} + # # TODO: exercise the cookie parser with bad input... diff --git a/tests/http_persistent.test b/tests/http_persistent.test index 136b0f0b..531d72eb 100644 --- a/tests/http_persistent.test +++ b/tests/http_persistent.test @@ -27,6 +27,8 @@ test http-persist-1 {simple setup} -constraints {serverListen} -body { } return $result +} -cleanup { + unset -nocomplain result d k bytes connection contentLength } -result {connection keep-alive content-length 3 connection keep-alive content-length 4} @@ -46,7 +48,8 @@ test http-persist-2 {three requests in two chunks} -constraints {serverListen} - } return $result - +} -cleanup { + unset -nocomplain result d k bytes contentLength } -result {content-length 3 content-length 4 content-length 5} @@ -65,7 +68,8 @@ test http-persist-3 {three requests in one chunk} -constraints {serverListen} -b } return $result - +} -cleanup { + unset -nocomplain result d k bytes contentLength } -result {content-length 3 content-length 4 content-length 5} @@ -86,7 +90,8 @@ test http-persist-4 {three requests in three chunks, broken strangely} -constrai } return $result - +} -cleanup { + unset -nocomplain result d k bytes contentLength } -result {content-length 3 content-length 4 content-length 5} @@ -106,7 +111,8 @@ test http-persist-5 {three requests in two chunks, one with content} -constraint } return $result - +} -cleanup { + unset -nocomplain result d k bytes contentLength } -result {content-length 3 content-length 4 content-length 5} @@ -125,7 +131,8 @@ test http-persist-6 {two requests in one chunk, both with content} -constraints } return $result - +} -cleanup { + unset -nocomplain result d k bytes contentLength } -result {content-length 3 content-length 4} @@ -144,7 +151,8 @@ test http-persist-7 {two requests in one chunk, both with content, trailing junk } return $result - +} -cleanup { + unset -nocomplain result d k bytes contentLength } -result {content-length 3 content-length 4 content-length {}} @@ -168,7 +176,8 @@ test http-persist-8 {one request in six chunks} -constraints {serverListen} -bod } return $result - +} -cleanup { + unset -nocomplain result d k bytes contentLength } -result {content-length 3} @@ -189,5 +198,6 @@ test http-persist-9 {two requests with content, broken to 3 arbitrary chunks} -c } return $result - +} -cleanup { + unset -nocomplain result d k bytes contentLength } -result {content-length 3 content-length 4} diff --git a/tests/ns_cache.test b/tests/ns_cache.test index 76d4354b..53adee55 100644 --- a/tests/ns_cache.test +++ b/tests/ns_cache.test @@ -342,7 +342,7 @@ test cache-7.1 {cache stats entries} -body { lsort [array names stats] } -cleanup { unset -nocomplain stats -} -result {entries expired flushed hitrate hits maxsize missed pruned saved size} +} -result {commit entries expired flushed hitrate hits maxsize missed pruned rollback saved size} test cache-7.2 {cache stats contents} -body { ns_cache_eval c1 k1 {return a} @@ -494,9 +494,6 @@ test cache-10.5 {memoize flush glob} -body { - - - test ns_cache-11.1 {stability} -constraints stress -body { ns_log Notice Begin cache stress test. @@ -535,6 +532,136 @@ test ns_cache-11.1 {stability} -constraints stress -body { +test ns_cache-12.1 {transaction rollback} -body { + + ns_cache_create trans_c1 1024 + + # + # set some value in the cache before the transactions + # + ns_cache_eval trans_c1 k1 {return 1} + # + # start the transaction + # + ns_cache_transaction_begin + # + # get some old value in the transaction + # + lappend result [ns_cache_get trans_c1 k1] + + # + # set three values in the transaction via ns_cache_eval + # + ns_cache_eval trans_c1 k2 {return 2} + ns_cache_eval trans_c1 k3 {return 3} + lappend result [ns_cache_incr trans_c1 k4] + + # + # check, if we can obtain the just fresh value inside the transaction + + # ... via _eval + lappend result [ns_cache_eval trans_c1 k2 {return 2}] + + # ... via _get + lappend result [ns_cache_get trans_c1 k2] + lappend result [ns_cache_get trans_c1 k3] + + # get keys inside transaction + lappend result [ns_cache_keys trans_c1 k2] + lappend result [ns_cache_keys trans_c1] + + # flush the value added by this transaction inside the transaction + lappend result [ns_cache_flush trans_c1 k3] + + # increment k4 once again, get the value via ns_cache_get + lappend result [ns_cache_incr trans_c1 k4] + lappend result [ns_cache_get trans_c1 k4] + + # + # rollback the transaction + # + ns_cache_transaction_rollback + + # + # get the all the entries from the cache + # + lappend result [ns_cache_keys trans_c1] + + # + # k2 should not be obtainable (catch -> 1) + lappend result [catch {ns_cache_get trans_c1 k2}] + +} -cleanup { + unset -nocomplain result + ns_cache_flush trans_c1 +} -result {1 1 2 2 3 k2 {k1 k2 k3 k4} 1 2 2 k1 1} + + +test ns_cache-12.2 {transaction commit} -body { + + ns_cache_create trans_c1 1024 + + # + # set some value in the cache before the transactions + # + ns_cache_eval trans_c1 k1 {return 1} + + # + # start the transaction + # + ns_cache_transaction_begin + # + # get some old value in the transaction + # + lappend result [ns_cache_get trans_c1 k1] + + # + # set three values in the transaction via ns_cache_eval + # + ns_cache_eval trans_c1 k2 {return 2} + ns_cache_eval trans_c1 k3 {return 3} + lappend result [ns_cache_incr trans_c1 k4] + + # + # check, if we can obtain the just fresh value inside the transaction + + # ... via _eval + lappend result [ns_cache_eval trans_c1 k2 {return 2}] + + # ... via _get + lappend result [ns_cache_get trans_c1 k2] + lappend result [ns_cache_get trans_c1 k3] + + # get keys inside transaction + lappend result [ns_cache_keys trans_c1 k2] + lappend result [ns_cache_keys trans_c1] + + # flush the value added by this transaction inside the transaction + lappend result [ns_cache_flush trans_c1 k3] + + # increment k4 once again, get the value via ns_cache_get + lappend result [ns_cache_incr trans_c1 k4] + lappend result [ns_cache_get trans_c1 k4] + + # + # commit the transaction + # + ns_cache_transaction_commit + + # + # get the all the entries from the cache + # + lappend result [ns_cache_keys trans_c1] + + # + # k2 should be obtainable (catch -> 0) + lappend result [catch {ns_cache_get trans_c1 k2}] + +} -cleanup { + unset -nocomplain result + ns_cache_flush trans_c1 +} -result {1 1 2 2 3 k2 {k1 k2 k3 k4} 1 2 2 {k1 k2 k4} 0} + cleanupTests diff --git a/tests/ns_conn.test b/tests/ns_conn.test index 1bc18cff..48dcf38a 100644 --- a/tests/ns_conn.test +++ b/tests/ns_conn.test @@ -76,7 +76,7 @@ test ns_connchan-1.0 {basic operation} -body { test ns_connchan-1.1 {basic operation} -body { ns_connchan x -} -returnCodes error -result {bad subcmd "x": must be callback, close, detach, exists, list, open, read, or write} +} -returnCodes error -result {bad subcmd "x": must be callback, close, detach, exists, list, listen, open, read, or write} test ns_connchan-1.2 {detach without connection} -body { ns_connchan detach diff --git a/tests/ns_conn_host.test b/tests/ns_conn_host.test index 6c1c187d..ae0a5327 100644 --- a/tests/ns_conn_host.test +++ b/tests/ns_conn_host.test @@ -5,33 +5,40 @@ namespace import -force ::tcltest::* ::tcltest::configure {*}$argv +#ns_logctl severity Debug(ns:driver) on if {[ns_config test listenport]} { testConstraint serverListen true } - +set port [ns_config "test" listenport] test ns_conn_location-1.1 {default location} -constraints serverListen -setup { - ns_register_proc GET /location {ns_return 200 text/plain [ns_conn location] ;#} + ns_register_proc GET /location { + ns_return 200 text/plain [ns_conn location] + } } -body { nstest::http -getbody 1 -- GET /location } -cleanup { ns_unregister_op GET /location -} -result {200 http://test} +} -result [list 200 http://test:$port] -test ns_conn_location-2.1 {virtual host location} -constraints serverListen -body { - nstest::http -getbody 1 -setheaders {Host testvhost} -- GET /location -} -result {200 http://testvhost} +test ns_conn_location-2.1 {virtual host location} -constraints serverListen -setup { + ns_register_proc GET /location { + ns_return 200 text/plain [ns_conn location] + } +} -body { + nstest::http -getbody 1 -setheaders [list Host testvhost:$port] -- GET /location +} -result [list 200 http://testvhost:$port] # 2.2: Test bad host headers..? test ns_conn_location-3.1 {custom location} -constraints serverListen -body { - nstest::http -getbody 1 -setheaders {Host testvhost2} -- GET /location + nstest::http -getbody 1 -setheaders [list Host testvhost2:$port] -- GET /location } -result {200 testlocation.arg} diff --git a/tests/ns_info.test b/tests/ns_info.test index a31c3d0d..69f0ef31 100644 --- a/tests/ns_info.test +++ b/tests/ns_info.test @@ -6,11 +6,11 @@ namespace import -force ::tcltest::* ::tcltest::configure {*}$argv test ns_info-1.1 {basic syntax: plain call} -body { - ns_info + ns_info } -returnCodes error -result {wrong # args: should be "ns_info option"} test ns_info-1.2 {basic syntax: wrong argument} -body { - ns_info ? + ns_info ? } -returnCodes error -result {bad option "?": must be address, argv0, boottime, builddate, callbacks, config, home, hostname, ipv6, locks, log, major, minor, mimetypes, name, nsd, pagedir, pageroot, patchlevel, pid, platform, pools, scheduled, server, servers, sockcallbacks, ssl, tag, tcllib, threads, uptime, version, winnt, filters, traces, requestprocs, url2file, shutdownpending, or started} test ns_info-2.1.1 {basic operation} -body { @@ -19,7 +19,7 @@ test ns_info-2.1.1 {basic operation} -body { } -result 1 test ns_info-2.2.1 {basic operation} -body { - ns_info argv0 + ns_info argv0 } -match glob -result *nsd test ns_info-2.3.1 {basic operation} -body { @@ -27,7 +27,7 @@ test ns_info-2.3.1 {basic operation} -body { } -result 1 test ns_info-2.4.1 {basic operation builddate: positive if builddate = day of test} -body { - foreach {month day year blub time} [split [ns_info builddate]] {break} + foreach {month day year blub time} [split [ns_info builddate]] {break} regexp {[0-9]{10}} [clock scan "$month $day $year"] match } -result 1 @@ -36,33 +36,33 @@ test ns_info-2.5.1 {basic operation} -body { } -result 1 test ns_info-2.6.1 {basic operation} -body { - ns_info config + ns_info config } -match "glob" -result "*.nscfg" test ns_info-2.7.1 {basic operation} -body { - expr {[string length [ns_info home]]>1} + expr {[string length [ns_info home]]>1} } -result 1 test ns_info-2.8.1 {basic operation} -setup { - set hostname_by_exec [exec hostname] + set hostname_by_exec [exec hostname] } -body { - string match [ns_info hostname] $hostname_by_exec + string match [ns_info hostname] $hostname_by_exec } -result 1 test ns_info-2.9.1 {basic operation} -body { - expr {[llength [ns_info locks]]>0} + expr {[llength [ns_info locks]]>0} } -result 1 test ns_info-2.10.1 {basic operation} -body { - expr {[file tail [ns_info log]] ne ""} + expr {[file tail [ns_info log]] ne ""} } -result 1 test ns_info-2.11.1 {basic operation} -body { - ns_info major + ns_info major } -result 4 test ns_info-2.12.1 {basic operation} -body { - ns_info minor + ns_info minor } -result 99 test ns_info-2.12.2 {mimetypes} -body { @@ -70,19 +70,19 @@ test ns_info-2.12.2 {mimetypes} -body { } -match regexp -result {\.[A-z0-9]+} test ns_info-2.13.1 {basic operation} -body { - ns_info name + ns_info name } -result "NaviServer" test ns_info-2.14.1 {basic operation} -body { - file tail [ns_info nsd] + file tail [ns_info nsd] } -match glob -result "*nsd" test ns_info-2.16.1 {basic operation} -body { - ns_info patchlevel + ns_info patchlevel } -match "glob" -result "4.99.*" test ns_info-2.17.1 {basic operation} -body { - expr {[ns_info pid] == [pid]} + expr {[ns_info pid] == [pid]} } -result 1 #test ns_info-2.18.1 {basic operation} -body { @@ -90,8 +90,8 @@ test ns_info-2.17.1 {basic operation} -body { #} -result 1 test ns_info-2.19.1 {basic operation} -body { - expr {[llength [ns_info pools]] == 0 \ - || [string match thread* [lindex [ns_info pools] 0 0]]} + expr {[llength [ns_info pools]] == 0 + || [string match thread* [lindex [ns_info pools] 0 0]]} } -result 1 test ns_info-2.20.1 {basic operation} -body { @@ -99,15 +99,15 @@ test ns_info-2.20.1 {basic operation} -body { } -result 1 test ns_info-2.21.1 {basic operation} -body { - ns_info server + ns_info server } -result "test" test ns_info-2.22.1 {basic operation} -body { - ns_info servers + ns_info servers } -match "regexp" -result "testvhost|testvhost2|test" test ns_info-2.23.1 {basic operation} -body { - ns_info sockcallbacks + ns_info sockcallbacks } -result "" test ns_info-2.24.1 {basic operation} -body { @@ -119,25 +119,25 @@ test ns_info-2.26.1 {basic operation} -body { } -result 1 test ns_info-2.27.1 {basic operation} -body { - set expected_threads 0 - foreach _thread [ns_info threads] { - switch -- [lindex $_thread 0] { - "-driver-" - - "-sched-" - - "-main-" { set expected_threads 1 } - "default" {} - } - } - set expected_threads + set expected_threads 0 + foreach _thread [ns_info threads] { + switch -- [lindex $_thread 0] { + "-driver-" - + "-sched-" - + "-main-" { set expected_threads 1 } + "default" {} + } + } + set expected_threads } -result 1 test ns_info-2.28.1 {basic operation} -body { - ns_sleep 2 - expr {[ns_info uptime]>1} + ns_sleep 2 + expr {[ns_info uptime]>1} } -result 1 test ns_info-2.29.1 {basic operation} -body { - ns_info version + ns_info version } -result "4.99" #test ns_info-2.30.1 {basic operation} -body { diff --git a/tests/ns_pagepath.test b/tests/ns_pagepath.test index 030e3e45..d541549f 100644 --- a/tests/ns_pagepath.test +++ b/tests/ns_pagepath.test @@ -9,12 +9,11 @@ namespace import -force ::tcltest::* ::tcltest::configure {*}$argv - set serverroot [file join [ns_config "test" home] \ [ns_config "ns/server/testvhost/fastpath" serverdir] ] set pagedir [ns_config "ns/server/testvhost/fastpath" pagedir] set vhosts [ns_config "ns/server/testvhost/vhost" hostprefix] - +set port [ns_config "test" listenport] test ns_pagepath-1.1 {basic path} -body { @@ -42,29 +41,29 @@ test ns_pagepath-2.2 {host path} -body { test ns_pagepath-3.1 {virtual host path} -body { - nstest::http -getbody 1 -setheaders {Host testvhost} -- GET /pagepath + nstest::http -getbody 1 -setheaders [list Host testvhost:$port] -- GET /pagepath } -result [list 200 ${serverroot}/${vhosts}/t/e/s/testvhost/${pagedir}] test ns_pagepath-3.2 {virtual host path} -body { - nstest::http -getbody 1 -setheaders {Host testvhost} -- GET /pagepath?path=x + nstest::http -getbody 1 -setheaders [list Host testvhost:$port] -- GET /pagepath?path=x } -result [list 200 ${serverroot}/${vhosts}/t/e/s/testvhost/${pagedir}/x] test ns_pagepath-4.1 {custom serverroot} -body { - nstest::http -getbody 1 -setheaders {Host testvhost2} -- GET /pagepath + nstest::http -getbody 1 -setheaders [list Host testvhost2:$port] -- GET /pagepath } -result [list 200 testserverroot/arg/${pagedir}] test ns_pagepath-4.2 {custom serverroot} -body { - nstest::http -getbody 1 -setheaders {Host testvhost2} -- GET /pagepath?host=example.com + nstest::http -getbody 1 -setheaders [list Host testvhost2:$port] -- GET /pagepath?host=example.com } -result [list 200 testserverroot/example.com/arg/${pagedir}] test ns_pagepath-4.3 {custom serverroot} -body { - nstest::http -getbody 1 -setheaders {Host testvhost2} -- GET /pagepath?path=x + nstest::http -getbody 1 -setheaders [list Host testvhost2:$port] -- GET /pagepath?path=x } -result [list 200 testserverroot/arg/${pagedir}/x] test ns_pagepath-4.4 {custom serverroot} -body { - nstest::http -getbody 1 -setheaders {Host testvhost2} -- GET /pagepath?host=example.com&path=x + nstest::http -getbody 1 -setheaders [list Host testvhost2:$port] -- GET /pagepath?host=example.com&path=x } -result [list 200 testserverroot/example.com/arg/${pagedir}/x] diff --git a/tests/ns_server.test b/tests/ns_server.test index 64862926..70f8d277 100644 --- a/tests/ns_server.test +++ b/tests/ns_server.test @@ -34,6 +34,66 @@ test ns_server-1.5 {provide invalid server argument} -body { } -returnCodes error -result {invalid server: 'foo'} +# +# Testing ns_server active|all|queued +# + +test ns_server-2.0.0 {provide valid "ns_server active" argument} -body { + ns_server active +} -match exact -result "" + +test ns_server-2.0.1 {provide valid "ns_server active" argument} -body { + ns_server active -checkforproxy +} -match exact -result "" + +test ns_server-2.0.1 {provide invalid "ns_server active" argument} -body { + ns_server active -dummy +} -returnCodes error -result {wrong # args: should be "ns_server active ?-checkforproxy?"} + +test ns_server-2.0.3 {get values from an active connection} -setup { + ns_register_proc GET /run { + ns_return 200 text/plain [ns_server active] + } +} -body { + nstest::http -http 1.1 -getbody 1 GET /run +} -cleanup { + ns_unregister_op GET /run +} -match glob -result "200 *cns*running GET /run *" + +test ns_server-2.0.4 {get values from an active connection with -checkforproxy} -setup { + ns_register_proc GET /run { + ns_return 200 text/plain [ns_server active -checkforproxy] + } +} -body { + nstest::http -http 1.1 -getbody 1 GET /run +} -cleanup { + ns_unregister_op GET /run +} -match glob -result "200 *cns*running GET /run *" + +test ns_server-2.0.5 {get queued values from an active connection with -checkforproxy} -setup { + ns_register_proc GET /run { + ns_return 200 text/plain [ns_server queued] + } +} -body { + nstest::http -http 1.1 -getbody 1 GET /run +} -cleanup { + ns_unregister_op GET /run +} -match glob -result "200" + +test ns_server-2.0.6 {get all values from an active connection with -checkforproxy} -setup { + ns_register_proc GET /run { + ns_return 200 text/plain [ns_server all] + } +} -body { + nstest::http -http 1.1 -getbody 1 GET /run +} -cleanup { + ns_unregister_op GET /run +} -match glob -result "200 *cns*running GET /run *" + +# +# Continue with "basic" tests +# + test ns_server-2.1 {basic operation} -body { string is int -strict [ns_server connections] } -match exact -result 1 diff --git a/tests/ns_serverpath.test b/tests/ns_serverpath.test index 8f0c6e3f..26da0c88 100644 --- a/tests/ns_serverpath.test +++ b/tests/ns_serverpath.test @@ -20,6 +20,7 @@ namespace import -force ::tcltest::* set serverroot [file join [ns_config "test" home] \ [ns_config "ns/server/testvhost/fastpath" serverdir]] set vhosts [ns_config "ns/server/testvhost/vhost" hostprefix] +set port [ns_config "test" listenport] test ns_serverpath-1.1 {basic path} -body { @@ -47,49 +48,49 @@ test ns_serverpath-2.1 {host path} -body { test ns_serverpath-3.1 {virtual host path} -body { - nstest::http -getbody 1 -setheaders {Host testvhost} -- GET /serverpath + nstest::http -getbody 1 -setheaders [list Host testvhost:$port] -- GET /serverpath } -result [list 200 ${serverroot}/${vhosts}/t/e/s/testvhost] test ns_serverpath-3.2 {virtual host path} -body { - nstest::http -getbody 1 -setheaders {Host testvhost} -- GET /serverpath?host=example.com + nstest::http -getbody 1 -setheaders [list Host testvhost:$port] -- GET /serverpath?host=example.com } -result [list 200 ${serverroot}/${vhosts}/e/x/a/example.com] test ns_serverpath-3.3 {virtual host path} -body { - nstest::http -getbody 1 -setheaders {Host testvhost} -- GET /serverpath?host=EXAMPLE.COM + nstest::http -getbody 1 -setheaders [list Host testvhost:$port] -- GET /serverpath?host=EXAMPLE.COM } -result [list 200 ${serverroot}/${vhosts}/e/x/a/example.com] test ns_serverpath-3.4 {virtual host path} -body { - nstest::http -getbody 1 -setheaders {Host testvhost} -- GET /serverpath?host=www.example.com + nstest::http -getbody 1 -setheaders [list Host testvhost:$port] -- GET /serverpath?host=www.example.com } -result [list 200 ${serverroot}/${vhosts}/e/x/a/example.com] test ns_serverpath-3.5 {virtual host path} -body { - nstest::http -getbody 1 -setheaders {Host testvhost} -- GET /serverpath?host=www.example.com:80 + nstest::http -getbody 1 -setheaders [list Host testvhost:$port] -- GET /serverpath?host=www.example.com:80 } -result [list 200 ${serverroot}/${vhosts}/e/x/a/example.com] test ns_serverpath-3.6 {virtual host path} -body { - nstest::http -getbody 1 -setheaders {Host testvhost} -- GET /serverpath?host=1 + nstest::http -getbody 1 -setheaders [list Host testvhost:$port] -- GET /serverpath?host=1 } -result [list 200 ${serverroot}/${vhosts}/1/_/_/1] test ns_serverpath-3.7 {virtual host path} -body { - nstest::http -getbody 1 -setheaders {Host testvhost} -- GET /serverpath?host= + nstest::http -getbody 1 -setheaders [list Host testvhost:$port] -- GET /serverpath?host= } -result [list 200 ${serverroot}] test ns_serverpath-4.1 {custom serverroot} -constraints serverListen -body { - nstest::http -getbody 1 -setheaders {Host testvhost2} -- GET /serverpath + nstest::http -getbody 1 -setheaders [list Host testvhost2:$port] -- GET /serverpath } -result {200 testserverroot/arg} test ns_serverpath-4.2 {custom serverroot} -constraints serverListen -body { - nstest::http -getbody 1 -setheaders {Host testvhost2} -- GET /serverpath?host=example.com + nstest::http -getbody 1 -setheaders [list Host testvhost2:$port] -- GET /serverpath?host=example.com } -result {200 testserverroot/example.com/arg} test ns_serverpath-4.3 {custom serverroot} -constraints serverListen -body { - nstest::http -getbody 1 -setheaders {Host testvhost2} -- GET /serverpath?path=x + nstest::http -getbody 1 -setheaders [list Host testvhost2:$port] -- GET /serverpath?path=x } -result {200 testserverroot/arg/x} test ns_serverpath-4.4 {custom serverroot} -constraints serverListen -body { - nstest::http -getbody 1 -setheaders {Host testvhost2} -- GET /serverpath?host=example.com&path=x + nstest::http -getbody 1 -setheaders [list Host testvhost2:$port] -- GET /serverpath?host=example.com&path=x } -result {200 testserverroot/example.com/arg/x} diff --git a/tests/test.nscfg b/tests/test.nscfg index 76499410..2111266f 100644 --- a/tests/test.nscfg +++ b/tests/test.nscfg @@ -83,12 +83,11 @@ ns_param deferaccept 0 #ns_param writerstreaming true ;# false; activate writer for streaming HTML output (e.g. ns_writer) ns_section "ns/module/nssock/servers" +#ns_param test test:80 +#ns_param test \[::1\]:80 ns_param test test -ns_param test test:[ns_config "test" listenport] ns_param testvhost testvhost -ns_param testvhost testvhost:[ns_config "test" listenport] ns_param testvhost2 testvhost2 -ns_param testvhost2 testvhost2:[ns_config "test" listenport] ns_section "ns/mimetypes" ns_param .html "text/html"