--- /dev/null
+build-*
\ No newline at end of file
--- /dev/null
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+\f
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+\f
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+\f
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+\f
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+\f
+ Appendix: How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
--- /dev/null
+# -*- Makefile -*-
+#
+# This is a make+ file. Make+ is a set of scripts which enhance GNU
+# make and let you build RPMs, and other package types with just one
+# control file. To build this package you will need to download make+
+# from this site: http://www.annexia.org/freeware/makeplus/
+
+PACKAGE := rws
+VERSION_MAJOR := 1
+VERSION_MINOR := 2.0
+VERSION := $(VERSION_MAJOR).$(VERSION_MINOR)
+
+SUMMARY := Rich\'s Web Server
+COPYRIGHT := GNU LGPL
+AUTHOR := Richard W.M. Jones <rich@annexia.org>
+
+define DESCRIPTION
+RWS is a tiny, fast and elegant web server. It can serve simple files
+or run CGI scripts. It supports virtual hosts.
+endef
+
+RPM_REQUIRES := pthrlib >= 3.2.0, c2lib >= 1.3.0
+RPM_GROUP := System Environment/Daemons
+
+CFLAGS += -Wall -Werror -g -O2 -I$(includedir)/c2lib \
+ -I$(shell pg_config --includedir)
+ifneq ($(shell uname), SunOS)
+# Avoid a warning about reordering system include paths.
+CFLAGS += $(shell pcre-config --cflags)
+endif
+
+LIBS += -L$(libdir) -lpthrlib -lc2lib \
+ -L$(shell pg_config --libdir) -lpq \
+ $(shell pcre-config --libs)
+
+# make+ needs to support a better method of handling libraries than this:
+ifneq ($(shell uname), OpenBSD)
+ifneq ($(shell uname), FreeBSD)
+LIBS += -ldl
+endif
+endif
+
+ifeq ($(shell uname), SunOS)
+LIBS += -lnsl -lsocket
+endif
+
+LIBS += -lm
+
+OBJS := main.o cfg.o dir.o errors.o exec.o exec_so.o file.o mime_types.o \
+ process_rq.o rewrite.o
+HEADERS := $(srcdir)/rws_request.h
+
+all: build
+
+configure:
+ $(MP_CONFIGURE_START)
+ $(MP_CHECK_LIB) precomp c2lib
+ $(MP_CHECK_LIB) current_pth pthrlib
+ $(MP_CHECK_FUNCS) dlclose dlerror dlopen dlsym glob globfree \
+ putenv setenv
+ $(MP_CHECK_HEADERS) alloca.h arpa/inet.h dirent.h dlfcn.h fcntl.h \
+ glob.h grp.h netinet/in.h pwd.h setjmp.h signal.h string.h \
+ sys/mman.h sys/socket.h sys/stat.h sys/syslimits.h sys/types.h \
+ sys/wait.h syslog.h time.h unistd.h
+ $(MP_CONFIGURE_END)
+
+build: librws.a librws.so rwsd manpages conffiles syms \
+ examples/hello.so examples/show_params.so
+
+# Program.
+
+rwsd: $(OBJS)
+ $(CC) $(CFLAGS) $^ -L. -lrws $(LIBS) -o $@
+
+# Library.
+
+librws.a: rws_request.o
+ $(MP_LINK_STATIC) $@ $^
+
+librws.so: rws_request.lo
+ $(MP_LINK_DYNAMIC) $@ $^
+
+# Examples.
+
+examples/%.so: examples/%.lo
+ifneq ($(shell uname), SunOS)
+ $(CC) $(CFLAGS) -shared -Wl,-soname,$@ $^ -L. -lrws $(LIBS) -o $@
+else
+# XXX make+ needs to support this.
+ $(CC) $(CFLAGS) -shared -Wl,-h,$@ $^ -L. -lrws $(LIBS) -o $@
+endif
+
+# Build the manual pages.
+
+manpages: $(srcdir)/*.h
+ if cdoc; then \
+ rm -f *.3; \
+ cdoc \
+ --author '$(AUTHOR)' \
+ --license '$(COPYRIGHT)' \
+ --version '$(PACKAGE)-$(VERSION)' \
+ $^; \
+ fi
+
+# Build the configuration files.
+
+conffiles: conf/default conf/rws.conf
+
+conf/default: conf/default.in
+ sed 's,@pkgdatadir@,$(pkgdatadir),g' < $^ > $@
+
+conf/rws.conf: conf/rws.conf.in
+ sed 's,@pkgdatadir@,$(pkgdatadir),g' < $^ > $@
+
+# Build the symbols table.
+
+syms: rwsd.syms librws.syms
+
+rwsd.syms: rwsd
+ nm $< | sort | grep -i '^[0-9a-f]' | awk '{print $$1 " " $$3}' > $@
+
+librws.syms: librws.so
+ nm $< | sort | grep -i '^[0-9a-f]' | awk '{print $$1 " " $$3}' > $@
+
+# Run the simple test.
+
+test: test_rws.sh
+ LD_LIBRARY_PATH=.:$(LD_LIBRARY_PATH) $(MP_RUN_TESTS) $^
+
+install:
+ install -d $(DESTDIR)$(sbindir)
+ install -d $(DESTDIR)$(libdir)
+ install -d $(DESTDIR)$(includedir)
+ install -d $(DESTDIR)$(sysconfdir)/init.d
+ install -d $(DESTDIR)$(sysconfdir)/rws
+ install -d $(DESTDIR)$(sysconfdir)/rws/hosts
+ install -d $(DESTDIR)$(pkgdatadir)/symtabs
+ install -d $(DESTDIR)$(pkgdatadir)/so-bin
+ install -d $(DESTDIR)$(pkgdatadir)/html
+ install -d $(DESTDIR)$(man1dir)
+ install -d $(DESTDIR)$(man3dir)
+
+ install -m 0755 rwsd $(DESTDIR)$(sbindir)
+
+ $(MP_INSTALL_STATIC_LIB) librws.a
+ $(MP_INSTALL_DYNAMIC_LIB) librws.so
+
+ install -m 0644 $(HEADERS) $(DESTDIR)$(includedir)
+
+ install -m 0755 $(srcdir)/rws.rc $(DESTDIR)$(sysconfdir)/init.d/rws
+
+ if [ ! -f $(DESTDIR)$(sysconfdir)/rws/rws.conf ]; then \
+ install -m 0644 conf/rws.conf $(DESTDIR)$(sysconfdir)/rws; \
+ fi
+ if [ ! -f $(DESTDIR)$(sysconfdir)/rws/hosts/default ]; then \
+ install -m 0644 conf/default $(DESTDIR)$(sysconfdir)/rws/hosts; \
+ fi
+ install -m 0644 *.syms $(DESTDIR)$(pkgdatadir)/symtabs
+ install -m 0755 examples/*.so $(DESTDIR)$(pkgdatadir)/so-bin
+ install -m 0644 $(srcdir)/html/*.html $(DESTDIR)$(pkgdatadir)/html
+ install -m 0644 $(srcdir)/rwsd.1 $(DESTDIR)$(man1dir)
+ install -m 0644 *.3 $(DESTDIR)$(man3dir)
+
+define WEBSITE
+<% include page_header.msp %>
+
+ <h1>$(PACKAGE) - $(SUMMARY)</h1>
+
+ <p>
+ <code>rws</code> is a small, fast web server written in
+ C. It uses the <a href="../pthrlib/">pthrlib</a>
+ threading/server library and the <a href="../c2lib/">c2lib</a>
+ library of Perl-like basics for C. These make it
+ amongst the smallest, fastest and most straightforwardly
+ written web servers available now.
+ </p>
+
+ <p>
+ As of version 1.0.11, the stripped binary is 27K and
+ <code>size</code> reports:
+ </p>
+
+<pre>
+ text data bss dec hex filename
+ 24332 828 88 25248 62a0 rwsd
+</pre>
+
+ <p>
+ Of course, most of the magic is in <a href="../pthrlib/">pthrlib</a>.
+ </p>
+
+ <p>
+ It supports a fairly minimal set of features so far:
+ </p>
+
+ <ul>
+ <li> Complies (mostly) with <code>HTTP/1.1</code>.
+ <li> Serves files and includes an <code>mmap(2)</code>
+ file cache.
+ <li> Directory listings.
+ <li> CGI script execution (NPH scripts only!)
+ <li> Virtual hosts and aliases.
+ <li> Shared object scripts: essentially CGI scripts
+ written in C which are dynamically linked into the
+ server memory at runtime. Very fast.
+ <li> Rewrite rules using regular expressions.
+ </ul>
+
+ <p>
+ <a href="doc/">There is extensive documentation and a tutorial here.</a>
+ </p>
+
+ <h1>Download</h1>
+
+ <table border="1">
+ <tr>
+ <th> File </th>
+ <th> Format </th>
+ <th> Contents </th>
+ </tr>
+ <tr>
+ <td> <a href="$(PACKAGE)-$(VERSION).tar.gz">$(PACKAGE)-$(VERSION).tar.gz</a> </td>
+ <td> tar.gz </td>
+ <td> Latest source distribution </td>
+ </tr>
+ <tr>
+ <td> <a href="$(PACKAGE)-$(VERSION)-1.i686.rpm">$(PACKAGE)-$(VERSION)-1.i686.rpm</a> </td>
+ <td> i686 binary RPM </td>
+ <td> Binary server, header files, man pages
+ for Red Hat Linux </td>
+ </tr>
+ <tr>
+ <td> <a href="$(PACKAGE)-$(VERSION)-1.src.rpm">$(PACKAGE)-$(VERSION)-1.src.rpm</a> </td>
+ <td> source RPM </td>
+ <td> Source files for Red Hat Linux </td>
+ </tr>
+ </table>
+
+ <p>
+ You must install the latest <a href="../pthrlib/">pthrlib</a>
+ and <a href="../c2lib/">c2lib</a> libraries first!
+ </p>
+
+ <!--
+ <p>
+ <a href="/cgi-bin/autopatch.pl?dir=rws">Patches between versions
+ ...</a>
+ </p>
+ -->
+
+ <h2>News</h2>
+
+<p>
+<b>Sat Feb 8 17:00:47 GMT 2003:</b>
+Ported to Solaris, OpenBSD and FreeBSD (thanks to
+<a href="http://www.azazel.net/">Jeremy Sowden</a>
+and <a href="http://www.mondaymorning.org/">Richard Baker</a>
+for help and equipment).
+Added a test script which actually starts up and runs
+rws and verifies static file serving, CGI scripts and shared
+object scripts. Added <code>-ldl</code> to <code>LIBS</code>
+(thanks to jeffrey at box-sol.com). Build fixes for RH 7.3.
+</p>
+
+ <p>
+ <b>Sun Dec 8 16:07:20 GMT 2002:</b>
+ Enabled debugging and optimisations. Converted to use
+ <a href="../makeplus/">make+</a>. Changed to support
+ changes in the <code>pthrlib</code> API. The
+ <code>-f</code> option prevents the server from
+ changing directory as well. More descriptive thread
+ names. Give idle threads a different name.
+ </p>
+
+ <p>
+ <b>Mon Nov 25 09:31:37 GMT 2002:</b>
+ Added a symbols file for full symbol resolution in monolith.
+ Added <code>debian/conffiles</code> so that Debian package
+ won't splat configuration files.
+ Changed <code>process_rq</code> to set thread name to the
+ canonical path (useful for debugging, stats).
+ Set a maximum number of requests that we will service with
+ a single thread.
+ Enable stack traces.
+ </p>
+
+ <p>
+ <b>Sun Nov 17 23:31:32 GMT 2002:</b> Debian packages. Added MSP files.
+ <code>rws_request_*</code> symbols are now in a separate
+ library. Added <code>rwsd.1</code> manual page. RWS now
+ forces connection close on bad requests. Multiple fixes
+ to the way directories/subdirectories are handled. <code>exec_so</code>
+ catches <code>pth_die</code> exceptions and prints a
+ message. More rewrite fixes. Change the default root
+ to <code>/var/www</code>. Added <code>postinst</code> script
+ for Debian.
+ </p>
+
+ <p>
+ <b>Thu Nov 14 15:33:29 GMT 2002:</b> Major checkpoint release
+ for Monolith.
+ </p>
+
+ <p>
+ <b>Sun Oct 20 14:57:29 BST 2002:</b> Allow empty entries
+ in rws configuration file. Correct compilation flags for
+ PCRE. Allow the stack size to be selected from the configuration
+ file. Fixed path parsing. Added include files to RPM. Added
+ example MSP configuration to configuration file.
+ </p>
+
+ <p>
+ <b>Tue Oct 15 23:40:42 BST 2002:</b> Multiple bug fixes.
+ </p>
+
+ <p>
+ <b>Sun Oct 13 19:04:16 BST 2002:</b> Added
+ <code>-a</code>, <code>-d</code> and <code>-f</code>
+ flags which allow you to run the server on only
+ a single interface and make it simpler to debug.
+ (Thanks to Steve Atkins, steve at blighty com, for
+ this patch). You need the most recent <a href="../pthrlib/">pthrlib</a>.
+ </p>
+
+ <p>
+ <b>Sun Oct 6 13:00:39 BST 2002:</b> New <q>rewrite</q>
+ module allows comprehensive URL rewriting. Updated to use
+ the newest version of <a href="../c2lib/">c2lib</a>.
+ </p>
+
+ <p>
+ <b>Sat Sep 7 15:51:10 BST 2002:</b> Packages are now
+ available as i686 binary RPMs and source RPMs.
+ </p>
+
+ <h2>Old news and old versions</h2>
+
+ <p>
+ <b>Sat Aug 31 17:39:36 BST 2002</b>
+ </p>
+
+ <p>
+ <a href="rws-1.0.0.tar.gz">rws-1.0.0.tar.gz</a> released.
+ This includes a tutorial.
+ </p>
+
+ <p>
+ <b>Thu Aug 22 13:20:32 BST 2002</b>
+ </p>
+
+ <p>
+ <a href="rws-0.9.6.tar.gz">rws-0.9.6.tar.gz</a> released.
+ This includes manual pages.
+ </p>
+
+ <p>
+ <b>Thu Aug 22 12:27:16 BST 2002</b>
+ </p>
+
+ <p>
+ <a href="rws-0.9.5.tar.gz">rws-0.9.5.tar.gz</a> released.
+ I have changed the interface to shared object scripts to
+ allow me to extend it in the future without ever changing
+ it again (hopefully :-) See the README file and
+ <rws_request.h> for more
+ details.
+ </p>
+
+ <p>
+ <b>Wed Aug 21 14:20:12 BST 2002</b>
+ </p>
+
+ <p>
+ <a href="rws-0.9.4.tar.gz">rws-0.9.4.tar.gz</a> released.
+ Support for shared object scripts.
+ </p>
+
+ <p>
+ <b>Thu Jun 21 23:14:48 BST 2001</b>
+ </p>
+
+ <p>
+ <a href="rws-0.9.2.tar.gz">rws-0.9.2.tar.gz</a> released.
+ Directory listings are sorted alphabetically. Server
+ signature is printed at the bottom of directory listings.
+ Make sure you have <a href="../c2lib/">c2lib >= 1.2.12</a>.
+ </p>
+
+ <p>
+ <b>Tue May 22 14:22:06 BST 2001</b>
+ </p>
+
+ <p>
+ <a href="rws-0.0.9.tar.gz">rws-0.0.9.tar.gz</a> released.
+ URL paths are now unescaped correctly.
+ Make sure you have <a href="../pthrlib/">pthrlib >= 2.0.5</a>.
+ </p>
+
+ <p>
+ <b>Tue May 22 11:37:10 BST 2001</b>
+ </p>
+
+ <p>
+ <a href="rws-0.0.8.tar.gz">rws-0.0.8.tar.gz</a> released.
+ Added configuration files and init scripts to RPM.
+ Don't hold file descriptors open for files in the mmapped file cache.
+ Fixes to example rws.conf file. Fixed the mmapped file cache
+ so it no longer grows indefinitely (:-)
+ Make sure you have <a href="../pthrlib/">pthrlib >= 2.0.4</a>.
+ </p>
+
+ <p>
+ <b>Tue Apr 10 16:04:41 BST 2001</b>
+ </p>
+
+ <p>
+ <a href="rws-0.0.7.tar.gz">rws-0.0.7.tar.gz</a> released.
+ Generates <code>access_log</code> file. Directory
+ listings have been improved considerably.
+ </p>
+
+ <p>
+ <b>Mon Apr 9 17:34:31 BST 2001</b>
+ </p>
+
+ <p>
+ <a href="rws-0.0.6.tar.gz">rws-0.0.6.tar.gz</a> released.
+ This is the first public version.
+ </p>
+
+<% include page_footer.msp %>
+
+endef
+
+upload_website:
+ scp $(PACKAGE)-$(VERSION).tar.gz $(PACKAGE)-$(VERSION)-1.*.rpm \
+ $(PACKAGE)-$(VERSION).bin.tar.gz \
+ 10.0.0.248:annexia.org/freeware/$(PACKAGE)/
+ scp index.html \
+ 10.0.0.248:annexia.org/freeware/$(PACKAGE)/index.msp
+
+.PHONY: build configure test upload_website
--- /dev/null
+rws - a fast, small and efficient web server written in C
+written by Richard Jones <rich@annexia.org>
+
+GENERAL INFO
+------------
+
+``rws'' is yet another web server. This one offers the advantage
+that it is both cleanly written internally, as well as simple,
+and, of course, very fast. Current features include:
+
+* Serves files.
+* Memory-mapped file cache.
+* Serves directory listings.
+* Supports virtual hosts.
+* Supports aliases.
+* CGI scripts (NPH scripts only).
+* Shared object scripts (see below).
+* Access and error logs.
+
+The following are the features that I intend to add before I
+declare that ``rws'' is complete:
+
+* Run CGI scripts as a local user (similar to Apache suexec).
+* IP-based access control.
+* Manual pages.
+
+INSTALLATION INSTRUCTIONS
+-------------------------
+
+You must install c2lib and pthrlib libraries first. Get these
+from:
+
+ c2lib http://www.annexia.org/freeware/c2lib/
+ pthrlib http://www.annexia.org/freeware/pthrlib/
+
+If you are having problems with ``rws'', make sure that you
+have the latest versions of these libraries installed before
+reporting any errors to me.
+
+Build it:
+
+ ./configure --sysconfdir=/etc [--prefix=/usr]
+ make+
+ make+ test
+
+Install it (as root):
+
+ make+ install
+
+As configured above the server installs itself as
+/usr/local/sbin/rwsd, and expects to find configuration files in the
+/etc/rws directory (unless you specify different --prefix and
+--sysconfdir parameters).
+
+Edit /etc/rws/rws.conf (which is the global level configuration file)
+as appropriate for your site.
+
+Edit /etc/rws/hosts/default to configure as required. Note that by
+default, the document root is set to /usr/local/share/rws/html, which
+just contains a demo page.
+
+Start up the web server:
+
+ /usr/local/sbin/rwsd
+
+These are early days. If you find a bug, please report it to me
+by mailing rich@annexia.org. Please include a complete description.
+
+FAQ
+---
+
+1. The web server seems to have loads of memory leaks! It just sits
+ there sucking up more and more memory.
+
+ A. Not so. The web server stores a cache of memory mapped files,
+ making it quicker to serve the same file subsequent times.
+ Unfortunately top(1) or ps(1) count the memory mapped files
+ against the size of the process, even though they are never
+ really loaded into memory. To get a real picture of what's
+ going on, look at /proc/<<PID>>/maps and /proc/<<PID>>/fd/
+ (replace <<PID>> with the process ID of ``rws'').
+
+2. What's all this about shared object scripts and .so files?
+
+ A. See doc/index.html
+
+3. Can I run rws under Apache?
+
+ A. Yes, using mod_proxy. More instructions to come, but this is the
+ recommended approach if you want to integrate monolith applications
+ with your existing Apache-driven site.
--- /dev/null
+Support for SMP
+---------------
+
+It's actually going to be quite easy to modify rws so that it supports
+SMP - even though the underlying threading library is lightweight user
+threads. The plan is as follows, I'm just waiting for the patch!
+
+Support should probably go directly into pthrlib/src/pthr_server.c so
+that it can benefit all pthrlib-based servers.
+
+At start up, based on some command line flag and/or autodetection of
+the number of CPUs, the server should fork N times where N is the
+number of CPUs. This results in N+1 processes. Process 0 (the parent)
+will have the listening socket. Processes 1-N will be connected by
+Unix domain sockets back to process 0. Processes 1-N will have special
+modified listen threads listening on their respective Unix domain
+socket. (NB. Use the socketpair function to create each Unix domain
+socket).
+
+When process 0 receives a connection, based on some hashing function
+of the peername, it should decide which of the 1-N processes will
+handle the request. The important thing is that the hashing function
+must always assign the same client to the same process, which is why
+the hash should be based on the peername.
+
+When a new client connection arrives at process 0, let's say it
+decides to pass this to process n (1 <= n <= N). It should pass the
+file descriptor over the corresponding Unix domain socket to process n
+using the sendmsg(2) function (see cmsg(3) for details). Process n
+will receive the file descriptor and can then continue to handle the
+request.
+
+This method should scale up over small numbers of processors (eg. up
+to 8).
--- /dev/null
+#ifndef RWS_CONFIG_H
+#define RWS_CONFIG_H
+
+@TOP@
+
+#undef PACKAGE
+#undef VERSION
+
+@BOTTOM@
+
+#endif /* RWS_CONFIG_H */
--- /dev/null
+/* Configuration file parsing.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: cfg.c,v 1.11 2002/10/15 21:28:32 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+
+#ifdef HAVE_REGEX_H
+#include <regex.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <pool.h>
+#include <pstring.h>
+#include <hash.h>
+#include <vector.h>
+#include <pre.h>
+
+#include "re.h"
+#include "cfg.h"
+
+struct config_data
+{
+ sash data;
+
+ /* Aliases -- these are only present in the host-specific configuration
+ * files, not in CFG_MAIN.
+ */
+ shash aliases; /* Hash of string -> struct alias_data * */
+};
+
+struct alias_data
+{
+ sash data;
+};
+
+static pool cfg_pool = 0;
+static shash cfg_hosts; /* Hash of string -> struct config_data * */
+static struct config_data *cfg_main;
+
+static struct config_data *read_config (FILE *fp, int is_main, const char *filename);
+static void config_err (const char *filename, const char *line, const char *msg);
+
+/* The PATH argument will point to the base for configuration
+ * files, eg. "/etc/rws". We append "/rws.conf" to get the main
+ * configuration file and "/hosts/" to get the virtual hosts
+ * directory.
+ */
+void
+cfg_reread_config (const char *path)
+{
+ const char *config_file;
+ const char *hosts_dir;
+ FILE *fp;
+ DIR *dir;
+ struct dirent *d;
+ pool tmp;
+
+ /* Show the message about reloading the configuration file, but only
+ * the second and subsequent times this function is run.
+ */
+ if (cfg_pool)
+ fprintf (stderr, "reloading configuration file ...\n");
+
+ /* Remove any configuration data from previous configuration run. */
+ if (cfg_pool) delete_pool (cfg_pool);
+
+ /* Create new data structures. */
+ cfg_pool = new_subpool (global_pool);
+ tmp = new_subpool (cfg_pool);
+ cfg_hosts = new_shash (cfg_pool, struct config_data *);
+
+ config_file = psprintf (tmp, "%s/rws.conf", path);
+ hosts_dir = psprintf (tmp, "%s/hosts/", path);
+
+ /* Read in main configuration file. */
+ fp = fopen (config_file, "r");
+ if (fp == 0) { perror (config_file); exit (1); }
+ cfg_main = read_config (fp, 1, config_file);
+ fclose (fp);
+
+ /* Read in each virtual host configuration file. */
+ dir = opendir (hosts_dir);
+ if (dir)
+ {
+ while ((d = readdir (dir)) != 0)
+ {
+ struct config_data *c;
+
+ if (d->d_name[0] != '.') /* Ignore ".", ".." and dotfiles. */
+ {
+ const char *p;
+
+ p = psprintf (tmp, "%s/%s", hosts_dir, d->d_name);
+
+ fp = fopen (p, "r");
+ if (fp == 0) { perror (p); exit (1); }
+ c = read_config (fp, 0, p);
+ fclose (fp);
+
+ shash_insert (cfg_hosts, d->d_name, c);
+ }
+ }
+
+ closedir (dir);
+ }
+
+ delete_pool (tmp);
+}
+
+/* Read in a config file from FP. */
+static struct config_data *
+read_config (FILE *fp, int is_main, const char *filename)
+{
+ pool tmp = new_subpool (cfg_pool);
+ char *line = 0;
+ struct config_data *c;
+ struct alias_data *a = 0;
+
+ c = pmalloc (cfg_pool, sizeof *c);
+ c->data = new_sash (cfg_pool);
+ if (!is_main) c->aliases = new_shash (cfg_pool, struct alias_data *);
+ else c->aliases = 0;
+
+ while ((line = pgetlinec (tmp, fp, line)))
+ {
+ vector v;
+
+ if (!is_main && (v = prematch (tmp, line, re_alias_start, 0)))
+ {
+ const char *aliasname;
+
+ if (a) config_err (filename, line, "nested alias");
+
+ vector_get (v, 1, aliasname);
+
+ a = pmalloc (cfg_pool, sizeof *a);
+ a->data = new_sash (cfg_pool);
+
+ if (shash_insert (c->aliases, aliasname, a))
+ config_err (filename, line, "duplicate alias");
+ }
+ else if (!is_main && prematch (tmp, line, re_alias_end, 0))
+ {
+ if (!a)
+ config_err (filename, line,
+ "end alias found, but not inside an alias definition");
+
+ a = 0;
+ }
+ else if ((v = prematch (tmp, line, re_begin, 0)))
+ {
+ const char *key;
+ char *value, *end_line;
+ sash s;
+
+ vector_get (v, 1, key);
+
+ /* Read the data lines until we get to 'end key' line. */
+ value = pstrdup (tmp, "");
+ end_line = psprintf (tmp, "end %s", key);
+ while ((line = pgetlinec (tmp, fp, line)))
+ {
+ if (strcmp (line, end_line) == 0)
+ break;
+
+ value = pstrcat (tmp, value, line);
+ value = pstrcat (tmp, value, "\n");
+ }
+
+ if (!line)
+ config_err (filename, "EOF", "missing end <key> line");
+
+ if (a) s = a->data;
+ else s = c->data;
+
+ if (sash_insert (s, key, value))
+ config_err (filename, line, "duplicate definition");
+ }
+ else if ((v = prematch (tmp, line, re_conf_line, 0)))
+ {
+ const char *key, *value;
+ sash s;
+
+ vector_get (v, 1, key);
+ vector_get (v, 2, value);
+
+ /* 'key:' means define key as the empty string (as opposed to
+ * commenting it out which leaves 'key' undefined).
+ */
+ if (value == 0) value = "";
+
+ if (a) s = a->data;
+ else s = c->data;
+
+ if (sash_insert (s, key, value))
+ config_err (filename, line, "duplicate definition");
+ }
+ else
+ config_err (filename, line, "unexpected line");
+ }
+
+ delete_pool (tmp);
+
+ return c;
+}
+
+static void
+config_err (const char *filename, const char *line, const char *msg)
+{
+ fprintf (stderr,
+ "rws: %s: %s\n"
+ "rws: near ``%s''\n",
+ filename, msg,
+ line);
+ exit (1);
+}
+
+void *
+cfg_get_host (const char *host)
+{
+ struct config_data *c = 0;
+
+ shash_get (cfg_hosts, host, c);
+ return c;
+}
+
+void *
+cfg_get_alias (void *host_ptr, const char *path)
+{
+ struct config_data *c = (struct config_data *) host_ptr;
+ struct alias_data *a = 0;
+
+ shash_get (c->aliases, path, a);
+ return a;
+}
+
+const char *
+cfg_get_string (void *host_ptr, void *alias_ptr,
+ const char *key, const char *default_value)
+{
+ struct config_data *c = (struct config_data *) host_ptr;
+ struct alias_data *a = (struct alias_data *) alias_ptr;
+ const char *value;
+
+ if (a && sash_get (a->data, key, value))
+ return value;
+ if (c && sash_get (c->data, key, value))
+ return value;
+ if (sash_get (cfg_main->data, key, value))
+ return value;
+
+ return default_value;
+}
+
+int
+cfg_get_int (void *host_ptr, void *alias_ptr, const char *key, int default_value)
+{
+ const char *value = cfg_get_string (host_ptr, alias_ptr, key, 0);
+ int r;
+
+ if (!value) return default_value;
+
+ if (sscanf (value, "%d", &r) != 1) return default_value;
+
+ return r;
+}
+
+int
+cfg_get_bool (void *host_ptr, void *alias_ptr, const char *key, int default_value)
+{
+ const char *value = cfg_get_string (host_ptr, alias_ptr, key, 0);
+
+ if (!value) return default_value;
+
+ if (value[0] == '0' ||
+ value[0] == 'f' || value[0] == 'F' ||
+ value[0] == 'n' || value[0] == 'N' ||
+ (value[0] == 'o' && value[1] == 'f') ||
+ (value[0] == 'O' && value[1] == 'F'))
+ return 0;
+ else if (value[0] == '1' ||
+ value[0] == 't' || value[0] == 'T' ||
+ value[0] == 'y' || value[0] == 'Y' ||
+ (value[0] == 'o' && value[1] == 'n') ||
+ (value[0] == 'O' && value[1] == 'N'))
+ return 1;
+ else
+ return default_value;
+}
--- /dev/null
+/* Configuration file parsing.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: cfg.h,v 1.3 2001/03/24 17:26:28 rich Exp $
+ */
+
+#ifndef CFG_H
+#define CFG_H
+
+#include <hash.h>
+
+/* Reread the configuration file. */
+extern void cfg_reread_config (const char *path);
+
+/* If there is host matching HOST, return an opaque pointer to the host's
+ * configuration data.
+ */
+extern void *cfg_get_host (const char *host);
+
+/* If there is an alias exactly matching PATH for host HOST_PTR, return
+ * an opaque pointer to the alias's configuration data.
+ */
+extern void *cfg_get_alias (void *host_ptr, const char *path);
+
+/* Return the configuration string named KEY.
+ *
+ * HOST_PTR and ALIAS_PTR may be optionally given to narrow the search
+ * down to a particular host/alias combination.
+ *
+ * If the configuration string named KEY cannot be found, then DEFAULT_
+ * VALUE is returned instead.
+ */
+extern const char *cfg_get_string (void *host_ptr, void *alias_ptr,
+ const char *key,
+ const char *default_value);
+
+/* Similar to CFG_GET_STRING but the string is converted to an integer.
+ */
+extern int cfg_get_int (void *host_ptr, void *alias_ptr,
+ const char *key, int default_value);
+
+/* Similar to CFG_GET_STRING but the string is converted to a boolean.
+ */
+extern int cfg_get_bool (void *host_ptr, void *alias_ptr,
+ const char *key, int default_value);
+
+#endif /* CFG_H */
--- /dev/null
+# Example ``/etc/rws/hosts/<<HOSTNAME>>'' configuration file. You
+# need one of these files for every virtual host you serve (although
+# you can use symbolic links to save time) plus one file called ``default''
+# which is used for when the client doesn't send a ``Host:'' HTTP header.
+
+# The document root.
+
+alias /
+ # Path to the document root.
+ path: @pkgdatadir@/html
+
+ # Allow files to be viewed.
+ show: 1
+
+ # Do directory listings.
+ list: 1
+end alias
+
+# Example CGI directory.
+
+alias /cgi-bin/
+
+ path: /path/to/cgi/scripts
+
+ # Allow CGI scripts to be executed in here. Note that
+ # show and list are both off by default.
+
+ exec: 1
+
+end alias
+
+# Example shared object scripts directory (see doc/index.html).
+
+alias /so-bin/
+
+ path: @pkgdatadir@/so-bin
+
+ # Allow shared object scripts to be executed in here. Note
+ # that show and list are both of by default.
+
+ exec so: 1
+
+end alias
+
+# For Monolith, you need these aliases.
+
+alias /ml-styles/
+ path: @pkgdatadir@/ml-styles
+ show: 1
+end alias
+
+alias /ml-icons/
+ path: @pkgdatadir@/ml-icons
+ show: 1
+end alias
+
+# Rewrite rules applying to this host.
+
+begin rewrite
+
+# Rules apply in order. Use 'last' flag on a rule to cause execution
+# to finish at that rule if it matches.
+
+# Simple rewrite rule (external: the browser gets a redirect instruction).
+#^/default.html$ /index.html external
+
+# Simple rewrite rule (internal: browser is unaware of the redirect).
+#^/default.html$ /index.html
+
+# Monolith parsed pages (demonstrating the use of $1, $2, ... placeholders).
+# 'qsa' appends the original query string (if any) to the end of the
+# rewritten URL.
+#^/annexia/(.*\.msp)$ /so-bin/msp.so?page=$1 last,qsa
+#^/annexia/$ /annexia/index.msp qsa,external
+#^/annexia/(.*)/$ /annexia/$1/index.msp qsa,external
+
+# Conditional rewrite rules are not yet implemented.
+
+end rewrite
+
+# msp root: /home/rich/annexia
+# msp database: dbname=rich
+# monolith user database: dbname=rich
+# chatbot database: dbname=rich
--- /dev/null
+# Example ``/etc/rws/rws.conf'' file.
+
+# If the server is started as root, drop privileges and change to user
+# ID given below.
+#
+# Default: nobody
+#
+#user: web
+
+# Set the path to search for the mime.types file.
+#
+# Default: /etc/mime.types
+#
+#mime types file: /etc/httpd/conf/mime.types
+
+# The place to put the web server error log.
+#
+# Default: /tmp/error_log
+#
+#error log: /var/log/httpd/error_log
+
+# The place to put the web server access log.
+#
+# Default: /tmp/access_log
+#
+#access log: /var/log/httpd/access_log
+
+# Requests time out after this many seconds.
+#
+# Default: 60
+#
+#request timeout: 300
+
+# The email address of the maintainer, displayed in error messages.
+#
+# Default: (none)
+#
+#maintainer: bob@example.com
+
+# The default expiry time. This has the form '[+|-]NN[s|m|h|d|y]', for
+# example, '+1d' means set the expiry for current time + 1 day. The
+# default is to send no Expires: headers, but setting this to a small
+# value such as 1 day can dramatically improve the performance of your
+# website, especially given that rws currently doesn't implement the
+# GET/If-Modified-Since requirement of RFC 2616.
+#
+# Default: (none)
+#
+expires: +1d
+
+# Icons used in directory listings.
+
+icon for application/*: /icons/binary.gif 20x22 "Application"
+icon for application/x-tar: /icons/tar.gif 20x22 "Unix tape archive file"
+icon for application/x-gzip: /icons/compressed.gif 20x22 "Compressed file"
+icon for application/zip: /icons/compressed.gif 20x22 "Compressed file"
+icon for audio/*: /icons/sound1.gif 20x22 "Audio file"
+icon for image/*: /icons/image2.gif 20x22 "Image"
+icon for message/*: /icons/quill.gif 20x22 "Mail message"
+icon for text/*: /icons/text.gif 20x22 "Text file"
+icon for video/*: /icons/movie.gif 20x22 "Video file"
+
+no type icon: /icons/generic.gif 20x22 "File"
+
+unknown icon: /icons/unknown.gif 20x22 "Unknown file type"
+
+directory icon: /icons/dir.gif 20x22 "Directory"
+
+link icon: /icons/link.gif 20x22 "Symbolic link"
+
+special icon: /icons/sphere2.gif 20x22 "Special file"
--- /dev/null
+#!/bin/sh -
+#
+# This is make+. Make+ is a set of scripts which enhance GNU make and
+# let you build RPMs, and other packages types with just one control
+# file. Read more at http://www.annexia.org/freeware/makeplus/
+#
+# The original author is Richard W.M. Jones <rich@annexia.org>.
+#
+# This software has been explicitly placed in the PUBLIC DOMAIN. You
+# do not need any sort of license or agreement to use or copy this
+# software. You may also copyright this software yourself, and/or
+# relicense it under any terms you want, at any time and at no cost.
+# This allows you (among other things) to include this software with
+# other packages so that the user does not need to download and
+# install make+ separately.
+
+mp_options=""
+
+usage ()
+{
+ cat <<EOF
+./configure [--options]
+
+Installation directory options:
+ --prefix=PREFIX Installation prefix [default: /usr/local]
+ --sysconfdir=SYSCONF Installation prefix for configuration files
+ [default: PREFIX/etc]
+ --localstatedir=STATE Installation prefix for writable files
+ [default: PREFIX/var]
+
+Help options:
+ --help Display this help and exit.
+ --print-mp-cmd Display the make+ command and exit (use as final arg)
+EOF
+ exit 1
+}
+
+while [ $# -gt 0 ]; do
+ opt=$1 ; shift
+
+ case "$opt" in
+ --help)
+ usage
+ ;;
+ --print-mp-cmd)
+ echo "rm -f build-\*/config.mk"
+ echo "make+ $mp_options configure"
+ exit 0
+ ;;
+ --*prefix|--*dir)
+ opt=`echo $opt | sed 's/^--//'`
+ arg=$1 ; shift
+ mp_options="$mp_options $opt=$arg"
+ ;;
+ --*prefix=*|--*dir=*)
+ opt=`echo $opt | sed 's/^--//'`
+ mp_options="$mp_options $opt"
+ ;;
+ *)
+ mp_options="$mp_options $opt"
+ ;;
+ esac
+done
+
+rm -f build-*/config.mk
+make+ $mp_options configure
--- /dev/null
+/* Directory serving.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: dir.c,v 1.13 2003/02/05 23:02:51 rich Exp $
+ */
+
+#include "config.h"
+
+#include <assert.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+
+#ifdef HAVE_SYS_SYSLIMITS_H
+#include <sys/syslimits.h>
+#endif
+
+#include <pool.h>
+#include <pstring.h>
+#include <vector.h>
+#include <pre.h>
+
+#include <pthr_pseudothread.h>
+#include <pthr_http.h>
+#include <pthr_iolib.h>
+
+#include "process_rq.h"
+#include "mime_types.h"
+#include "file.h"
+#include "errors.h"
+#include "cfg.h"
+#include "re.h"
+#include "dir.h"
+
+static void choose_icon (process_rq p,
+ const char *filename, const struct stat *statbuf,
+ const char **icon, const char **icon_alt,
+ int *icon_width, int *icon_height);
+static void standard_icon (process_rq p, const char *name,
+ const char **icon, const char **icon_alt,
+ int *icon_width, int *icon_height);
+static void unknown_icon (process_rq p,
+ const char **icon, const char **icon_alt,
+ int *icon_width, int *icon_height);
+static int parse_icon_str (process_rq p, const char *icon_str,
+ const char **icon, const char **icon_alt,
+ int *icon_width, int *icon_height);
+static const char *get_printable_size (process_rq p,
+ const struct stat *statbuf);
+static const char *get_link_field (process_rq p, const char *filename);
+
+static int
+my_strcmp (const char **p1, const char **p2)
+{
+ return strcmp (*p1, *p2);
+}
+
+int
+dir_serve (process_rq p)
+{
+ http_response http_response;
+ int close, i;
+ char *index_file;
+ struct stat index_statbuf;
+ DIR *dir;
+ struct dirent *d;
+ vector files;
+
+ /* Is there an index file in this directory? If so, internally redirect
+ * the request to that file.
+ */
+ index_file = psprintf (p->pool, "%s/index.html", p->file_path);
+ if (stat (index_file, &index_statbuf) == 0 &&
+ S_ISREG (index_statbuf.st_mode))
+ {
+ /* Update the request structure appropriately. */
+ p->file_path = index_file;
+ p->remainder = psprintf (p->pool, "%s/index.html", p->remainder);
+ p->statbuf = index_statbuf;
+
+ /* Serve the file. */
+ return file_serve (p);
+ }
+
+ /* Are we allowed to generate a directory listing? */
+ if (!cfg_get_bool (p->host, p->alias, "list", 0))
+ return bad_request_error (p, "directory listing not allowed");
+
+ /* Yes: read the files into a local vector. */
+ dir = opendir (p->file_path);
+ if (dir == 0)
+ return bad_request_error (p, "error opening directory");
+
+ files = new_vector (p->pool, const char *);
+ while ((d = readdir (dir)) != 0)
+ {
+ if (d->d_name[0] != '.') /* Ignore hidden files. */
+ {
+ char *name = pstrdup (p->pool, d->d_name);
+ vector_push_back (files, name);
+ }
+ }
+ closedir (dir);
+
+ /* Sort them into alphabetical order. */
+ psort (files, my_strcmp);
+
+ /* Not changed, so it's a real cache hit. */
+ http_response = new_http_response (p->pool, p->http_request, p->io,
+ 200, "OK");
+ http_response_send_headers (http_response,
+ /* Content type. */
+ "Content-Type", "text/html",
+ /* End of headers. */
+ NULL);
+ close = http_response_end_headers (http_response);
+
+ if (http_request_is_HEAD (p->http_request)) return close;
+
+ io_fprintf (p->io,
+ "<html><head><title>Directory listing: %s</title></head>" CRLF
+ "<body bgcolor=\"#ffffff\">" CRLF
+ "<h1>Directory listing: %s</h1>" CRLF
+ "<a href=\"..\">Go up to parent directory</a>" CRLF
+ "<table border=\"0\">" CRLF,
+ p->canonical_path, p->canonical_path);
+
+ for (i = 0; i < vector_size (files); ++i)
+ {
+ const char *name, *pathname, *icon, *icon_alt;
+ const char *size = "", *link_field = "";
+ int icon_width, icon_height;
+ struct stat file_statbuf;
+
+ vector_get (files, i, name);
+
+ /* Generate the full pathname. */
+ pathname = psprintf (p->pool, "%s/%s", p->file_path, name);
+
+ /* Stat the file to get type and size information. */
+ if (lstat (pathname, &file_statbuf) == 0)
+ {
+ /* Choose an icon type. */
+ choose_icon (p, name, &file_statbuf, &icon, &icon_alt,
+ &icon_width, &icon_height);
+
+ /* Get the size. */
+ if (S_ISREG (file_statbuf.st_mode))
+ size = get_printable_size (p, &file_statbuf);
+
+ /* If it's a link, get the link field. */
+ if (S_ISLNK (file_statbuf.st_mode))
+ link_field = get_link_field (p, pathname);
+
+ /* Print the pathname. */
+ io_fprintf (p->io,
+ "<tr><td><img src=\"%s\" alt=\"%s\" width=\"%d\" height=\"%d\"></td><td><a href=\"%s%s\">%s</a> %s</td><td>%s</td></tr>" CRLF,
+ icon, icon_alt, icon_width, icon_height,
+ name,
+ S_ISDIR (file_statbuf.st_mode) ? "/" : "",
+ name,
+ link_field,
+ size);
+ }
+ }
+
+ io_fprintf (p->io,
+ "</table>" CRLF
+ "<hr>%s<br>" CRLF
+ "</body></html>" CRLF,
+ http_get_servername ());
+
+ return close;
+}
+
+static void
+choose_icon (process_rq p,
+ const char *filename, const struct stat *statbuf,
+ const char **icon, const char **icon_alt,
+ int *icon_width, int *icon_height)
+{
+ if (S_ISREG (statbuf->st_mode))
+ {
+ const char *mime_type = 0;
+ const char *icon_str;
+ vector v;
+
+ /* Get the file extension and map it to a MIME type. */
+ if ((v = prematch (p->pool, filename, re_ext, 0)) != 0)
+ {
+ char *ext;
+
+ vector_get (v, 1, ext);
+ mime_type = mime_types_get_type (ext);
+ }
+ if (!mime_type)
+ {
+ standard_icon (p, "no type",
+ icon, icon_alt, icon_width, icon_height);
+ return;
+ }
+
+ /* If there a icon specified for this MIME type? */
+ icon_str = cfg_get_string (p->host, p->alias,
+ psprintf (p->pool, "icon for %s", mime_type),
+ 0);
+ if (!icon_str)
+ {
+ /* Try looking for an icon for class / * instead. */
+ v = pstrcsplit (p->pool, mime_type, '/');
+ if (vector_size (v) >= 1)
+ {
+ const char *mime_class;
+
+ vector_get (v, 0, mime_class);
+ icon_str = cfg_get_string (p->host, p->alias,
+ psprintf (p->pool, "icon for %s/*",
+ mime_class), 0);
+ }
+ }
+
+ if (!icon_str)
+ {
+ unknown_icon (p, icon, icon_alt, icon_width, icon_height);
+ return;
+ }
+
+ /* Split up the string. */
+ if (!parse_icon_str (p, icon_str, icon, icon_alt, icon_width, icon_height))
+ {
+ fprintf (stderr,
+ "cannot parse icon description: %s (mime_type = %s)\n",
+ icon_str, mime_type);
+ unknown_icon (p, icon, icon_alt, icon_width, icon_height);
+ return;
+ }
+ }
+ else if (S_ISDIR (statbuf->st_mode))
+ {
+ standard_icon (p, "directory", icon, icon_alt, icon_width, icon_height);
+ }
+ else if (S_ISLNK (statbuf->st_mode))
+ {
+ standard_icon (p, "link", icon, icon_alt, icon_width, icon_height);
+ }
+ else
+ {
+ standard_icon (p, "special", icon, icon_alt, icon_width, icon_height);
+ }
+}
+
+static void
+standard_icon (process_rq p, const char *name,
+ const char **icon, const char **icon_alt,
+ int *icon_width, int *icon_height)
+{
+ const char *icon_str;
+
+ icon_str = cfg_get_string (p->host, p->alias,
+ psprintf (p->pool, "%s icon", name), 0);
+ if (!icon_str)
+ {
+ unknown_icon (p, icon, icon_alt, icon_width, icon_height);
+ return;
+ }
+
+ if (!parse_icon_str (p, icon_str, icon, icon_alt, icon_width, icon_height))
+ {
+ fprintf (stderr, "cannot parse icon description: %s\n", icon_str);
+ unknown_icon (p, icon, icon_alt, icon_width, icon_height);
+ return;
+ }
+}
+
+static void
+unknown_icon (process_rq p,
+ const char **icon, const char **icon_alt,
+ int *icon_width, int *icon_height)
+{
+ const char *icon_str;
+
+ icon_str = cfg_get_string (p->host, p->alias, "unknown icon", 0);
+ if (!icon_str)
+ {
+ fprintf (stderr,
+ "``unknown icon'' must be present in configuration file\n");
+ exit (1);
+ }
+
+ if (!parse_icon_str (p, icon_str, icon, icon_alt, icon_width, icon_height))
+ {
+ fprintf (stderr, "cannot parse icon description: %s\n", icon_str);
+ exit (1);
+ }
+}
+
+static int
+parse_icon_str (process_rq p, const char *icon_str,
+ const char **icon, const char **icon_alt,
+ int *icon_width, int *icon_height)
+{
+ vector v;
+ char *s;
+
+ /* Split up the string. */
+ if ((v = prematch (p->pool, icon_str, re_icon, 0)) == 0)
+ return 0;
+
+ if (vector_size (v) != 5) return 0;
+
+ vector_get (v, 1, *icon);
+ vector_get (v, 2, s);
+ sscanf (s, "%d", icon_width);
+ vector_get (v, 3, s);
+ sscanf (s, "%d", icon_height);
+ vector_get (v, 4, *icon_alt);
+
+ return 1;
+}
+
+static const char *
+get_printable_size (process_rq p,
+ const struct stat *statbuf)
+{
+ unsigned long size = statbuf->st_size;
+
+ if (size < 1024)
+ return psprintf (p->pool, "%lu bytes", size);
+ else if (size < 1024 * 1024)
+ return psprintf (p->pool, "%.1f KB", size / 1024.0);
+ else
+ return psprintf (p->pool, "%.1f MB", size / (1024 * 1024.0));
+}
+
+static const char *
+get_link_field (process_rq p, const char *filename)
+{
+ const char prefix[] = "-> ";
+ const int prefix_sz = sizeof prefix - 1;
+ char *buffer;
+ int n;
+
+#ifndef NAME_MAX
+ /* Solaris defines NAME_MAX on a per-filesystem basis.
+ * See: http://lists.spine.cx/archives/everybuddy/2002-May/001419.html
+ */
+ long NAME_MAX = pathconf (filename, _PC_NAME_MAX);
+#endif
+
+ buffer = pmalloc (p->pool, NAME_MAX + 1 + prefix_sz);
+
+ memcpy (buffer, prefix, prefix_sz);
+
+ n = readlink (filename, buffer + prefix_sz, NAME_MAX + 1);
+ if (n == -1) return "";
+
+ buffer[n + prefix_sz] = '\0';
+ return buffer;
+}
--- /dev/null
+/* Directory serving.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: dir.h,v 1.3 2001/03/24 17:26:28 rich Exp $
+ */
+
+#ifndef DIR_H
+#define DIR_H
+
+#include "config.h"
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#include <pool.h>
+
+#include <pthr_pseudothread.h>
+#include <pthr_http.h>
+#include <pthr_iolib.h>
+
+#include "process_rq.h"
+
+extern int dir_serve (process_rq p);
+
+#endif /* DIR_H */
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+ <title>rws documentation index</title>
+ <style type="text/css"><!--
+ h1 {
+ text-align: center;
+ }
+ pre {
+ background-color: #eeeeff;
+ }
+ code {
+ color: green;
+ font-weight: bold;
+ }
+ --></style>
+ </head>
+
+ <body bgcolor="#ffffff">
+ <h1>rws documentation index</h1>
+
+ <h2>Shared object scripts</h2>
+
+ <p>
+ Shared object scripts are a possibly unique feature of <code>rws</code>.
+ A shared object script is a CGI script, written in C, which
+ is loaded into the address space of the server at runtime.
+ Thus shared object scripts are very fast because they are
+ written in C, loaded just once, and able to run without
+ needing a <code>fork(2)</code>.
+ </p>
+
+ <p>
+ On the other hand, the penalty for speed is security, although
+ competent C programmers who are using all the features of
+ <a href="http://www.annexia.org/freeware/c2lib/">c2lib</a> and
+ <a href="http://www.annexia.org/freeware/pthrlib/">pthrlib</a>
+ should be able to write code which is free of buffer overflows
+ and some other common security issues. (However if you allow
+ your server to run shared object scripts from untrusted
+ third parties, then you have essentially no security at all, since
+ shared object scripts can interfere with the internal workings
+ of the webserver in arbitrary ways).
+ </p>
+
+ <h3>The anatomy of a shared object script</h3>
+
+ <p>
+ A shared object script is a <q><code>.so</code></q>
+ file (in other words, a shared library or <q>DLL</q>).
+ It should contain a single external symbol called
+ <code>handle_request</code>, prototyped as:
+ </p>
+
+<pre>
+int handle_request (rws_request rq);
+</pre>
+
+ <p>
+ The <code>rws_request</code> object is defined in
+ <code>rws_request.h</code>.
+ </p>
+
+ <p>
+ The first time that any client requests the shared
+ object script, <code>rws</code> calls <code>dlopen(3)</code>
+ on the file. As noted in the <code>dlopen(3)</code>
+ manual page, this will cause <code>_init</code> and any
+ constructor functions in the file to be run.
+ Then <code>rws</code> creates the <code>rws_request</code>
+ object (see below) and calls <code>handle_request</code>.
+ The shared object script remains loaded in memory
+ after <code>handle_request</code> has returned, ready
+ for the next invocation.
+ </p>
+
+ <p>
+ On subsequent invocations, <code>dlopen(3)</code> is
+ <em>not</em> called, so constructors only run once.
+ </p>
+
+ <p>
+ However, on each invocation, <code>rws</code> checks the
+ modification time of the file on disk, and if it has
+ changed, then it will attempt to reload the file. To
+ do this, it calls <code>dlclose(3)</code> first, which
+ will cause <code>_fini</code> and destructors in the
+ library to run, and unloads the library from memory. It
+ then reopens (<code>dlopen(3)</code>) the new file on
+ disk, as above. Beware that there are some occasions when
+ <code>rws</code> actually cannot reload a shared object
+ script, even though it notices that the file has changed
+ on disk. <code>rws</code> keeps a use count of the number
+ of threads currently using the shared object script, and
+ for safety reasons it cannot reload the file until this
+ usage count drops to zero. This means that in some cases
+ (eg. under very heavy load) a shared object script might
+ never be reloaded, even if it changes on disk.
+ </p>
+
+ <h3>Configuring rws to recognise shared object scripts</h3>
+
+ <p>
+ <code>rws</code> will not try to run shared object scripts
+ unless the <code>exec so</code> flag has been set on the
+ alias, and the shared object script itself is executable (mode 0755).
+ Here is an example shared object scripts directory:
+ </p>
+
+<pre>
+alias /so-bin/
+ path: /usr/share/rws/so-bin
+ exec so: 1
+end alias
+</pre>
+
+ <p>
+ Make sure that the <code>so-bin</code> directory is only
+ writable by trusted users, and make sure each shared object
+ script is executable, mode 0755.
+ </p>
+
+ <p>
+ If you can't make your shared object scripts run, then here
+ is a checklist before you email me:
+ </p>
+
+ <ul>
+ <li> Make sure you have put the above alias section into
+ the correct host file.
+ <li> <code>exec so</code> option is set?
+ <li> Restarted <code>rwsd</code>?
+ <li> Directory is world readable, executable (mode 0755)?
+ <li> Shared object script is world readable, executable (mode 0755)?
+ <li> Any unresolved symbols (<code>ldd -r script.so</code>), apart
+ from the <code>rws_request_*</code> symbols which will be resolved
+ when the library is loaded into <code>rws</code>?
+ <li> Missing <code>handle_request</code> function?
+ <li> <code>handle_request</code> is exported in the dynamic
+ symbol table (<code>nm -D script.so</code>)?
+ <li> Check the contents of your error_log file to see
+ if any error messages were reported.
+ </ul>
+
+ <p>
+ I have quite successfully used <code>gdb</code> on a running
+ server to debug and diagnose problems in shared object
+ scripts. However note that by default <code>gdb</code> may
+ have trouble loading the symbol table for your script. Use
+ the <code>sharedlibrary script.so</code>
+ command to load symbols instead.
+ </p>
+
+ <h3>Shared object scripts vs. Monolith applications</h3>
+
+ <p>
+ If you've been looking at the
+ <a href="http://www.annexia.org/freeware/monolith/">Monolith
+ application framework</a> pages, then you may be confused
+ about how shared object scripts relate to Monolith.
+ </p>
+
+ <p>
+ Shared object scripts are the direct analogy to CGI scripts,
+ the only difference being that CGI scripts are usually written
+ in very high level languages like Perl and PHP, and shared
+ object scripts are loaded into the server process for efficiency.
+ (Perl CGI scripts can also be loaded into the Apache
+ server process using <code>mod_perl</code>, and this is done
+ for similar reasons of efficiency).
+ </p>
+
+ <p>
+ Monolith programs are entire applications, the sort of
+ thing which normally would be written using dozens of
+ cooperating CGI scripts. In the case of Monolith, however,
+ the entire application compiles down to a single <code>.so</code>
+ file which happens to be (you guessed it) a shared object script.
+ </p>
+
+ <p>
+ Imagine that you are going to write yet another web-based email
+ client. For some reason you want to write this in C (please
+ don't try this at home: I wrote one in Perl at my last job and
+ that was hard enough). Here are three possible approaches
+ using C and <code>rws</code>:
+ </p>
+
+ <ol>
+ <li>
+ <p>
+ Write forty or so shared object scripts. Each displays
+ a single frame of the application, one might generate
+ the frameset, a couple of dozen to implement specific
+ operations like emptying trash or moving a message between
+ folders.
+ </p>
+ <p>
+ This is very much the normal way of writing CGI-based
+ applications.
+ </p>
+ <li> Write a Monolith application. This will probably be
+ in lots of C files, but will compile down and be linked
+ into a single <code>.so</code> file (eg. <code>email.so</code>)
+ which is dropped into the <code>so-bin</code> directory.
+ <li>
+ <p>
+ Write a Monolith email super-widget. This is going
+ to exist in a shared library called
+ <code>/usr/lib/libmyemail.so</code>
+ with a corresponding header file defining the interface
+ called <code>myemail.h</code>.
+ </p>
+ <p>
+ Write a tiny Monolith application which just instantiates
+ a window and an email widget, and embeds the email widget
+ in the window. This will compile into <code>email.so</code>
+ (it'll be very tiny) which is dropped into <code>so-bin</code>.
+ </p>
+ <p>
+ The advantage of this final approach is that you can
+ reuse the email widget in other places, or indeed sell
+ it to other Monolith users.
+ </p>
+ </ol>
+
+ <p>
+ So Monolith is good when you want to build applications
+ from widgets as you would if you were building a
+ Java/Swing, Windows MFC, gtk, Tcl/Tk graphical application.
+ It's also good if code re-use is important to you.
+ Shared object scripts are good when you are familiar with
+ CGI-based techniques to build websites.
+ </p>
+
+ <p>
+ Of course, the same <code>rws</code> server can serve
+ shared object scripts, multiple Monolith applications,
+ flat files, and directory listings, all at the same time.
+ </p>
+
+ <h3>Tutorial on writing shared object scripts</h3>
+
+ <p>
+ In this tutorial I will explain how the two shared object
+ script examples supplied with <code>rws</code> work. You
+ will also need to have read the tutorials for
+ <a href="http://www.annexia.org/freeware/c2lib/">c2lib</a> and
+ <a href="http://www.annexia.org/freeware/pthrlib/">pthrlib</a>
+ which you can find by going to their respective web pages.
+ </p>
+
+ <p>
+ The first example, <code>hello.c</code> is very simple indeed.
+ It's just a "hello world" program. The program starts by
+ including <code>rws_request.h</code>:
+ </p>
+
+<pre>
+#include <rws_request.h>
+</pre>
+
+ <p>
+ Following this is the <code>handle_request</code>
+ function. This is the function which <code>rws</code>
+ will call every time a user requests the script:
+ </p>
+
+<pre>
+int
+handle_request (rws_request rq)
+{
+ pseudothread pth = rws_request_pth (rq);
+ http_request http_request = rws_request_http_request (rq);
+ io_handle io = rws_request_io (rq);
+
+ int close;
+ http_response http_response;
+
+ /* Begin response. */
+ http_response = new_http_response (pth, http_request, io,
+ 200, "OK");
+ http_response_send_headers (http_response,
+ /* Content type. */
+ "Content-Type", "text/plain",
+ /* End of headers. */
+ NULL);
+ close = http_response_end_headers (http_response);
+
+ if (http_request_is_HEAD (http_request)) return close;
+
+ io_fprintf (io, "hello, world!");
+
+ return close;
+}
+</pre>
+
+ <p>
+ We first extract some fields from the <code>rws_request</code>
+ object. <code>rws</code> has already taken the time to
+ parse the HTTP headers from the client, but we need to
+ generate the reply headers (shared object scripts
+ are always "nph" -- no parsed headers). The
+ <code>pthrlib</code> functions
+ <code>new_http_response</code>,
+ <code>http_response_send_headers</code> and
+ <code>http_response_end_headers</code> do this. Note
+ that we send a <code>Content-Type: text/plain</code>
+ header. You must always generate a correct
+ <code>Content-Type</code> header.
+ </p>
+
+ <p>
+ If the original request was a <code>HEAD</code> request, then
+ the client only wants to see the headers, so we stop here.
+ </p>
+
+ <p>
+ Otherwise we generate our message and return.
+ </p>
+
+ <p>
+ NB. Don't call <code>io_fclose</code> on the I/O handle! If you
+ really want to force the connection to close, set the
+ <code>close</code> variable to 1 and return it. This is
+ because the client (or proxy) might be issuing several
+ separate HTTP requests over the same kept-alive TCP connection.
+ </p>
+
+ <p>
+ The second example, <code>show_params.c</code>, is just slightly
+ more complex, but demonstrates how to do parameter parsing.
+ After reading this you should have enough knowledge to
+ go away and write your own shared object scripts that
+ actually do useful stuff.
+ </p>
+
+ <p>
+ As before, we start by including a few useful headers:
+ </p>
+
+<pre>
+#include <pool.h>
+#include <vector.h>
+#include <pthr_cgi.h>
+
+#include <rws_request.h>
+</pre>
+
+ <p>
+ The <code>handle_request</code> function starts the same way
+ as before:
+ </p>
+
+<pre>
+int
+handle_request (rws_request rq)
+{
+ pool pool = rws_request_pool (rq);
+ pseudothread pth = rws_request_pth (rq);
+ http_request http_request = rws_request_http_request (rq);
+ io_handle io = rws_request_io (rq);
+</pre>
+
+ <p>
+ Then we define some variables that we're going to use:
+ </p>
+
+<pre>
+ cgi cgi;
+ int close, i;
+ http_response http_response;
+ vector headers, params;
+ struct http_header header;
+ const char *name, *value;
+</pre>
+
+ <p>
+ The actual job of parsing out the CGI parameters is simplified
+ because <code>pthrlib</code> contains a CGI library
+ (similar to Perl's <code>CGI.pm</code>):
+ </p>
+
+<pre>
+ /* Parse CGI parameters. */
+ cgi = new_cgi (pool, http_request, io);
+</pre>
+
+ <p>
+ The response phase begins by sending the HTTP
+ headers as before:
+ </p>
+
+<pre>
+ /* Begin response. */
+ http_response = new_http_response (pth, http_request, io,
+ 200, "OK");
+ http_response_send_headers (http_response,
+ /* Content type. */
+ "Content-Type", "text/plain",
+ /* End of headers. */
+ NULL);
+ close = http_response_end_headers (http_response);
+
+ if (http_request_is_HEAD (http_request)) return close;
+</pre>
+
+ <p>
+ Now we print out the actual contents of both the
+ <code>http_request</code> object and the <code>cgi</code>
+ object. HTTP headers first:
+ </p>
+
+<pre>
+ io_fprintf (io, "This is the show_params shared object script.\r\n\r\n");
+ io_fprintf (io, "Your browser sent the following headers:\r\n\r\n");
+
+ headers = http_request_get_headers (http_request);
+ for (i = 0; i < vector_size (headers); ++i)
+ {
+ vector_get (headers, i, header);
+ io_fprintf (io, "\t%s: %s\r\n", header.key, header.value);
+ }
+
+ io_fprintf (io, "----- end of headers -----\r\n");
+</pre>
+
+ <p>
+ The full URL (including the query string), the path alone,
+ the query string:
+ </p>
+
+<pre>
+ io_fprintf (io, "The URL was: %s\r\n",
+ http_request_get_url (http_request));
+ io_fprintf (io, "The path component was: %s\r\n",
+ http_request_path (http_request));
+ io_fprintf (io, "The query string was: %s\r\n",
+ http_request_query_string (http_request));
+ io_fprintf (io, "The query arguments were:\r\n");
+</pre>
+
+ <p>
+ Finally we print out the CGI parameters from the <code>cgi</code>
+ object:
+ </p>
+
+<pre>
+ params = cgi_params (cgi);
+ for (i = 0; i < vector_size (params); ++i)
+ {
+ vector_get (params, i, name);
+ value = cgi_param (cgi, name);
+ io_fprintf (io, "\t%s=%s\r\n", name, value);
+ }
+
+ io_fprintf (io, "----- end of parameters -----\r\n");
+
+ return close;
+}
+</pre>
+
+ <h2>Further examples</h2>
+
+ <p>
+ That's the end of this tutorial. I hope you enjoyed it. Please
+ contact the author about corrections or to obtain more information.
+ </p>
+
+ <h2>Links to manual pages</h2>
+
+ <ul>
+ <li> <a href="rws_request_canonical_path.3.html"><code>rws_request_canonical_path(3)</code></a> </li>
+ <li> <a href="rws_request_file_path.3.html"><code>rws_request_file_path(3)</code></a> </li>
+ <li> <a href="rws_request_host_header.3.html"><code>rws_request_host_header(3)</code></a> </li>
+ <li> <a href="rws_request_http_request.3.html"><code>rws_request_http_request(3)</code></a> </li>
+ <li> <a href="rws_request_io.3.html"><code>rws_request_io(3)</code></a> </li>
+ <li> <a href="rws_request_pool.3.html"><code>rws_request_pool(3)</code></a> </li>
+ <li> <a href="rws_request_pth.3.html"><code>rws_request_pth(3)</code></a> </li>
+ </ul>
+
+ <hr>
+ <address><a href="mailto:rich@annexia.org">Richard Jones</a></address>
+<!-- Created: Wed May 1 19:36:16 BST 2002 -->
+<!-- hhmts start -->
+Last modified: Wed Oct 9 20:02:40 BST 2002
+<!-- hhmts end -->
+ </body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>rws_request_pool</title>
+</head>
+<body>
+
+<h1 align=center>rws_request_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 15:31:08 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+rws_request_pool, rws_request_pth, rws_request_http_request, rws_request_io, rws_request_host_header, rws_request_canonical_path, rws_request_file_path - retrieve fields in rws_request object</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <rws_request.h>
+
+pool rws_request_pool (rws_request);
+pseudothread rws_request_pth (rws_request);
+http_request rws_request_http_request (rws_request);
+io_handle rws_request_io (rws_request);
+const char *rws_request_host_header (rws_request);
+const char *rws_request_canonical_path (rws_request);
+const char *rws_request_file_path (rws_request);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions retrieve the fields in an <b>rws_request</b>
+object. This object is passed to shared object scripts when
+they are invoked by rws as:</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>int handle_request (rws_request rq)</b></td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_pool</b> returns the per-request pool. If you
+wish to store data between requests, then use static
+variables, or create your own subpool of <b>global_pool</b>,
+or make allocations in <b>_init</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_pth</b> returns the currently running thread
+handle.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_http_request</b> returns the current HTTP
+request (see <b>new_http_request(3)</b>). To parse the CGI
+parameters, you need to call <b>new_cgi</b> (see
+<b>new_cgi(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_io</b> returns the IO handle connected to the
+browser.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_host_header</b> returns the contents of the
+HTTP <b>Host:</b> header, or the string <b>default</b> if
+none was given.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_canonical_path</b> returns the canonical path
+requested by the browser (after removing <b>..</b>,
+<b>//</b>, etc.), eg. <b>/so-bin/file.so</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_file_path</b> returns the actual path to the
+SO file being requested, eg.
+<b>/usr/share/rws/so-bin/file.so</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+rws-0.9.6</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_cgi(3)</b>, <b>new_http_response(3)</b>,
+<b>new_http_request(3)</b>, pthrlib tutorial, rws
+<b>examples/</b> directory.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>rws_request_pool</title>
+</head>
+<body>
+
+<h1 align=center>rws_request_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 15:31:08 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+rws_request_pool, rws_request_pth, rws_request_http_request, rws_request_io, rws_request_host_header, rws_request_canonical_path, rws_request_file_path - retrieve fields in rws_request object</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <rws_request.h>
+
+pool rws_request_pool (rws_request);
+pseudothread rws_request_pth (rws_request);
+http_request rws_request_http_request (rws_request);
+io_handle rws_request_io (rws_request);
+const char *rws_request_host_header (rws_request);
+const char *rws_request_canonical_path (rws_request);
+const char *rws_request_file_path (rws_request);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions retrieve the fields in an <b>rws_request</b>
+object. This object is passed to shared object scripts when
+they are invoked by rws as:</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>int handle_request (rws_request rq)</b></td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_pool</b> returns the per-request pool. If you
+wish to store data between requests, then use static
+variables, or create your own subpool of <b>global_pool</b>,
+or make allocations in <b>_init</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_pth</b> returns the currently running thread
+handle.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_http_request</b> returns the current HTTP
+request (see <b>new_http_request(3)</b>). To parse the CGI
+parameters, you need to call <b>new_cgi</b> (see
+<b>new_cgi(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_io</b> returns the IO handle connected to the
+browser.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_host_header</b> returns the contents of the
+HTTP <b>Host:</b> header, or the string <b>default</b> if
+none was given.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_canonical_path</b> returns the canonical path
+requested by the browser (after removing <b>..</b>,
+<b>//</b>, etc.), eg. <b>/so-bin/file.so</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_file_path</b> returns the actual path to the
+SO file being requested, eg.
+<b>/usr/share/rws/so-bin/file.so</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+rws-0.9.6</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_cgi(3)</b>, <b>new_http_response(3)</b>,
+<b>new_http_request(3)</b>, pthrlib tutorial, rws
+<b>examples/</b> directory.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>rws_request_pool</title>
+</head>
+<body>
+
+<h1 align=center>rws_request_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 15:31:08 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+rws_request_pool, rws_request_pth, rws_request_http_request, rws_request_io, rws_request_host_header, rws_request_canonical_path, rws_request_file_path - retrieve fields in rws_request object</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <rws_request.h>
+
+pool rws_request_pool (rws_request);
+pseudothread rws_request_pth (rws_request);
+http_request rws_request_http_request (rws_request);
+io_handle rws_request_io (rws_request);
+const char *rws_request_host_header (rws_request);
+const char *rws_request_canonical_path (rws_request);
+const char *rws_request_file_path (rws_request);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions retrieve the fields in an <b>rws_request</b>
+object. This object is passed to shared object scripts when
+they are invoked by rws as:</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>int handle_request (rws_request rq)</b></td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_pool</b> returns the per-request pool. If you
+wish to store data between requests, then use static
+variables, or create your own subpool of <b>global_pool</b>,
+or make allocations in <b>_init</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_pth</b> returns the currently running thread
+handle.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_http_request</b> returns the current HTTP
+request (see <b>new_http_request(3)</b>). To parse the CGI
+parameters, you need to call <b>new_cgi</b> (see
+<b>new_cgi(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_io</b> returns the IO handle connected to the
+browser.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_host_header</b> returns the contents of the
+HTTP <b>Host:</b> header, or the string <b>default</b> if
+none was given.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_canonical_path</b> returns the canonical path
+requested by the browser (after removing <b>..</b>,
+<b>//</b>, etc.), eg. <b>/so-bin/file.so</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_file_path</b> returns the actual path to the
+SO file being requested, eg.
+<b>/usr/share/rws/so-bin/file.so</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+rws-0.9.6</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_cgi(3)</b>, <b>new_http_response(3)</b>,
+<b>new_http_request(3)</b>, pthrlib tutorial, rws
+<b>examples/</b> directory.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>rws_request_pool</title>
+</head>
+<body>
+
+<h1 align=center>rws_request_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 15:31:08 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+rws_request_pool, rws_request_pth, rws_request_http_request, rws_request_io, rws_request_host_header, rws_request_canonical_path, rws_request_file_path - retrieve fields in rws_request object</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <rws_request.h>
+
+pool rws_request_pool (rws_request);
+pseudothread rws_request_pth (rws_request);
+http_request rws_request_http_request (rws_request);
+io_handle rws_request_io (rws_request);
+const char *rws_request_host_header (rws_request);
+const char *rws_request_canonical_path (rws_request);
+const char *rws_request_file_path (rws_request);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions retrieve the fields in an <b>rws_request</b>
+object. This object is passed to shared object scripts when
+they are invoked by rws as:</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>int handle_request (rws_request rq)</b></td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_pool</b> returns the per-request pool. If you
+wish to store data between requests, then use static
+variables, or create your own subpool of <b>global_pool</b>,
+or make allocations in <b>_init</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_pth</b> returns the currently running thread
+handle.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_http_request</b> returns the current HTTP
+request (see <b>new_http_request(3)</b>). To parse the CGI
+parameters, you need to call <b>new_cgi</b> (see
+<b>new_cgi(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_io</b> returns the IO handle connected to the
+browser.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_host_header</b> returns the contents of the
+HTTP <b>Host:</b> header, or the string <b>default</b> if
+none was given.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_canonical_path</b> returns the canonical path
+requested by the browser (after removing <b>..</b>,
+<b>//</b>, etc.), eg. <b>/so-bin/file.so</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_file_path</b> returns the actual path to the
+SO file being requested, eg.
+<b>/usr/share/rws/so-bin/file.so</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+rws-0.9.6</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_cgi(3)</b>, <b>new_http_response(3)</b>,
+<b>new_http_request(3)</b>, pthrlib tutorial, rws
+<b>examples/</b> directory.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>rws_request_pool</title>
+</head>
+<body>
+
+<h1 align=center>rws_request_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 15:31:08 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+rws_request_pool, rws_request_pth, rws_request_http_request, rws_request_io, rws_request_host_header, rws_request_canonical_path, rws_request_file_path - retrieve fields in rws_request object</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <rws_request.h>
+
+pool rws_request_pool (rws_request);
+pseudothread rws_request_pth (rws_request);
+http_request rws_request_http_request (rws_request);
+io_handle rws_request_io (rws_request);
+const char *rws_request_host_header (rws_request);
+const char *rws_request_canonical_path (rws_request);
+const char *rws_request_file_path (rws_request);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions retrieve the fields in an <b>rws_request</b>
+object. This object is passed to shared object scripts when
+they are invoked by rws as:</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>int handle_request (rws_request rq)</b></td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_pool</b> returns the per-request pool. If you
+wish to store data between requests, then use static
+variables, or create your own subpool of <b>global_pool</b>,
+or make allocations in <b>_init</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_pth</b> returns the currently running thread
+handle.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_http_request</b> returns the current HTTP
+request (see <b>new_http_request(3)</b>). To parse the CGI
+parameters, you need to call <b>new_cgi</b> (see
+<b>new_cgi(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_io</b> returns the IO handle connected to the
+browser.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_host_header</b> returns the contents of the
+HTTP <b>Host:</b> header, or the string <b>default</b> if
+none was given.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_canonical_path</b> returns the canonical path
+requested by the browser (after removing <b>..</b>,
+<b>//</b>, etc.), eg. <b>/so-bin/file.so</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_file_path</b> returns the actual path to the
+SO file being requested, eg.
+<b>/usr/share/rws/so-bin/file.so</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+rws-0.9.6</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_cgi(3)</b>, <b>new_http_response(3)</b>,
+<b>new_http_request(3)</b>, pthrlib tutorial, rws
+<b>examples/</b> directory.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>rws_request_pool</title>
+</head>
+<body>
+
+<h1 align=center>rws_request_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 15:31:09 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+rws_request_pool, rws_request_pth, rws_request_http_request, rws_request_io, rws_request_host_header, rws_request_canonical_path, rws_request_file_path - retrieve fields in rws_request object</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <rws_request.h>
+
+pool rws_request_pool (rws_request);
+pseudothread rws_request_pth (rws_request);
+http_request rws_request_http_request (rws_request);
+io_handle rws_request_io (rws_request);
+const char *rws_request_host_header (rws_request);
+const char *rws_request_canonical_path (rws_request);
+const char *rws_request_file_path (rws_request);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions retrieve the fields in an <b>rws_request</b>
+object. This object is passed to shared object scripts when
+they are invoked by rws as:</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>int handle_request (rws_request rq)</b></td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_pool</b> returns the per-request pool. If you
+wish to store data between requests, then use static
+variables, or create your own subpool of <b>global_pool</b>,
+or make allocations in <b>_init</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_pth</b> returns the currently running thread
+handle.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_http_request</b> returns the current HTTP
+request (see <b>new_http_request(3)</b>). To parse the CGI
+parameters, you need to call <b>new_cgi</b> (see
+<b>new_cgi(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_io</b> returns the IO handle connected to the
+browser.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_host_header</b> returns the contents of the
+HTTP <b>Host:</b> header, or the string <b>default</b> if
+none was given.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_canonical_path</b> returns the canonical path
+requested by the browser (after removing <b>..</b>,
+<b>//</b>, etc.), eg. <b>/so-bin/file.so</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_file_path</b> returns the actual path to the
+SO file being requested, eg.
+<b>/usr/share/rws/so-bin/file.so</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+rws-0.9.6</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_cgi(3)</b>, <b>new_http_response(3)</b>,
+<b>new_http_request(3)</b>, pthrlib tutorial, rws
+<b>examples/</b> directory.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>rws_request_pool</title>
+</head>
+<body>
+
+<h1 align=center>rws_request_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 15:31:09 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+rws_request_pool, rws_request_pth, rws_request_http_request, rws_request_io, rws_request_host_header, rws_request_canonical_path, rws_request_file_path - retrieve fields in rws_request object</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <rws_request.h>
+
+pool rws_request_pool (rws_request);
+pseudothread rws_request_pth (rws_request);
+http_request rws_request_http_request (rws_request);
+io_handle rws_request_io (rws_request);
+const char *rws_request_host_header (rws_request);
+const char *rws_request_canonical_path (rws_request);
+const char *rws_request_file_path (rws_request);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions retrieve the fields in an <b>rws_request</b>
+object. This object is passed to shared object scripts when
+they are invoked by rws as:</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>int handle_request (rws_request rq)</b></td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_pool</b> returns the per-request pool. If you
+wish to store data between requests, then use static
+variables, or create your own subpool of <b>global_pool</b>,
+or make allocations in <b>_init</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_pth</b> returns the currently running thread
+handle.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_http_request</b> returns the current HTTP
+request (see <b>new_http_request(3)</b>). To parse the CGI
+parameters, you need to call <b>new_cgi</b> (see
+<b>new_cgi(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_io</b> returns the IO handle connected to the
+browser.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_host_header</b> returns the contents of the
+HTTP <b>Host:</b> header, or the string <b>default</b> if
+none was given.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_canonical_path</b> returns the canonical path
+requested by the browser (after removing <b>..</b>,
+<b>//</b>, etc.), eg. <b>/so-bin/file.so</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rws_request_file_path</b> returns the actual path to the
+SO file being requested, eg.
+<b>/usr/share/rws/so-bin/file.so</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+rws-0.9.6</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_cgi(3)</b>, <b>new_http_response(3)</b>,
+<b>new_http_request(3)</b>, pthrlib tutorial, rws
+<b>examples/</b> directory.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+/* Deliver errors back to the user.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: errors.c,v 1.5 2002/12/01 14:58:01 rich Exp $
+ */
+
+#include "config.h"
+
+#include <pool.h>
+
+#include <pthr_pseudothread.h>
+#include <pthr_http.h>
+#include <pthr_iolib.h>
+
+#include "process_rq.h"
+#include "cfg.h"
+#include "errors.h"
+
+int
+bad_request_error (process_rq p, const char *text)
+{
+ http_response http_response;
+ int close;
+ const char *maintainer;
+
+ maintainer = cfg_get_string (p->host, p->alias,
+ "maintainer", "(no maintainer)"); /* XXX */
+
+ http_response = new_http_response (p->pool, p->http_request, p->io,
+ 500, "Internal server error");
+ http_response_send_headers (http_response,
+ /* Content type. */
+ "Content-Type", "text/html",
+ NO_CACHE_HEADERS,
+ /* End of headers. */
+ NULL);
+ close = http_response_end_headers (http_response);
+
+ if (http_request_is_HEAD (p->http_request)) return close;
+
+ /* XXX Escaping. */
+ io_fprintf (p->io,
+ "<html><head><title>Internal server error</title></head>" CRLF
+ "<body bgcolor=\"#ffffff\">" CRLF
+ "<h1>500 Internal server error</h1>" CRLF
+ "There was an error serving this request:" CRLF
+ "<pre>" CRLF
+ "%s" CRLF
+ "</pre>" CRLF
+ "<hr>" CRLF
+ "<address>%s</address>" CRLF
+ "</body></html>" CRLF,
+ text, maintainer);
+
+ /* It's always a good idea to force the connection to close after an
+ * error. This is particularly important with monolith applications
+ * after they have thrown an exception.
+ */
+ /* return close; */
+ return 1;
+}
+
+int
+file_not_found_error (process_rq p)
+{
+ http_response http_response;
+ int close;
+ const char *maintainer;
+
+ maintainer = cfg_get_string (p->host, p->alias,
+ "maintainer", "(no maintainer)");
+
+ http_response = new_http_response (p->pool, p->http_request, p->io,
+ 404, "File or directory not found");
+ http_response_send_headers (http_response,
+ /* Content type. */
+ "Content-Type", "text/html",
+ NO_CACHE_HEADERS,
+ /* End of headers. */
+ NULL);
+ close = http_response_end_headers (http_response);
+
+ if (http_request_is_HEAD (p->http_request)) return close;
+
+ io_fprintf (p->io,
+ "<html><head><title>File or directory not found</title></head>" CRLF
+ "<body bgcolor=\"#ffffff\">" CRLF
+ "<h1>404 File or directory not found</h1>" CRLF
+ "The file you requested was not found on this server." CRLF
+ "<hr>" CRLF
+ "<address>%s</address>" CRLF
+ "</body></html>" CRLF,
+ maintainer);
+
+ return close;
+}
+
+int
+moved_permanently (process_rq p, const char *location)
+{
+ http_response http_response;
+ int close;
+
+ http_response = new_http_response (p->pool, p->http_request, p->io,
+ 301, "Moved permanently");
+ http_response_send_headers (http_response,
+ /* Content length. */
+ "Content-Length", "0",
+ /* Location. */
+ "Location", location,
+ /* End of headers. */
+ NULL);
+ close = http_response_end_headers (http_response);
+
+ if (http_request_is_HEAD (p->http_request)) return close;
+
+ return close;
+}
--- /dev/null
+/* Deliver errors back to the user.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: errors.h,v 1.1 2001/03/24 17:26:29 rich Exp $
+ */
+
+#ifndef ERRORS_H
+#define ERRORS_H
+
+#include "config.h"
+
+#include <pool.h>
+
+#include <pthr_pseudothread.h>
+#include <pthr_http.h>
+#include <pthr_iolib.h>
+
+extern int bad_request_error (process_rq p, const char *text);
+extern int file_not_found_error (process_rq p);
+extern int moved_permanently (process_rq p, const char *location);
+
+#endif /* ERRORS_H */
--- /dev/null
+/* Simplest possible example of a shared object script.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: hello.c,v 1.3 2002/12/01 16:16:04 rich Exp $
+ */
+
+#include "rws_request.h"
+
+int
+handle_request (rws_request rq)
+{
+ http_request http_request = rws_request_http_request (rq);
+ io_handle io = rws_request_io (rq);
+
+ int close;
+ http_response http_response;
+
+ /* Begin response. */
+ http_response = new_http_response (pth_get_pool (current_pth),
+ http_request, io,
+ 200, "OK");
+ http_response_send_headers (http_response,
+ /* Content type. */
+ "Content-Type", "text/plain",
+ /* End of headers. */
+ NULL);
+ close = http_response_end_headers (http_response);
+
+ if (http_request_is_HEAD (http_request)) return close;
+
+ io_fprintf (io, "hello, world!");
+
+ return close;
+}
--- /dev/null
+/* More complex example shared object script showing parameter parsing.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: show_params.c,v 1.4 2002/12/01 16:16:04 rich Exp $
+ */
+
+#include <pool.h>
+#include <vector.h>
+#include <pthr_cgi.h>
+
+#include "rws_request.h"
+
+int
+handle_request (rws_request rq)
+{
+ pool pool = pth_get_pool (current_pth);
+ http_request http_request = rws_request_http_request (rq);
+ io_handle io = rws_request_io (rq);
+
+ cgi cgi;
+ int close, i;
+ http_response http_response;
+ vector headers, params;
+ struct http_header header;
+ const char *name, *value;
+
+ /* Parse CGI parameters. */
+ cgi = new_cgi (pool, http_request, io);
+
+ /* Begin response. */
+ http_response = new_http_response (pool, http_request, io,
+ 200, "OK");
+ http_response_send_headers (http_response,
+ /* Content type. */
+ "Content-Type", "text/plain",
+ /* End of headers. */
+ NULL);
+ close = http_response_end_headers (http_response);
+
+ if (http_request_is_HEAD (http_request)) return close;
+
+ io_fprintf (io, "This is the show_params shared object script.\r\n\r\n");
+ io_fprintf (io, "Your browser sent the following headers:\r\n\r\n");
+
+ headers = http_request_get_headers (http_request);
+ for (i = 0; i < vector_size (headers); ++i)
+ {
+ vector_get (headers, i, header);
+ io_fprintf (io, "\t%s: %s\r\n", header.key, header.value);
+ }
+
+ io_fprintf (io, "----- end of headers -----\r\n");
+
+ io_fprintf (io, "The URL was: %s\r\n",
+ http_request_get_url (http_request));
+ io_fprintf (io, "The path component was: %s\r\n",
+ http_request_path (http_request));
+ io_fprintf (io, "The query string was: %s\r\n",
+ http_request_query_string (http_request));
+ io_fprintf (io, "The query arguments were:\r\n");
+
+ params = cgi_params (cgi);
+ for (i = 0; i < vector_size (params); ++i)
+ {
+ vector_get (params, i, name);
+ value = cgi_param (cgi, name);
+ io_fprintf (io, "\t%s=%s\r\n", name, value);
+ }
+
+ io_fprintf (io, "----- end of parameters -----\r\n");
+
+ return close;
+}
--- /dev/null
+/* CGI scripts
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: exec.c,v 1.6 2003/02/05 23:02:51 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#include <pool.h>
+#include <pstring.h>
+
+#include <pthr_http.h>
+
+#include "process_rq.h"
+#include "errors.h"
+#include "exec.h"
+
+#ifndef HAVE_SETENV
+#ifdef HAVE_PUTENV
+/* setenv implementation for architectures which only have putenv. The
+ * apparent memory leak in this code does not actually matter because
+ * this is only called in the child process just before invoking exec.
+ */
+static inline void
+setenv (const char *name, const char *value, int overwrite)
+{
+ int len = strlen (name) + strlen (value) + 2;
+ char *str = malloc (len);
+
+ snprintf (str, len, "%s=%s", name, value);
+ putenv (str);
+}
+#else
+#error "no setenv or putenv in your libc"
+#endif
+#endif
+
+/* Note: For performance reasons and because I wanted to simplify the
+ * server, this code only handles NPH scripts.
+ *
+ * You must ensure that your CGI program can generate full NPH headers.
+ * This is generally quite simple. For example, with Perl's CGI.pm,
+ * do this:
+ *
+ * use CGI qw(:standard -nph);
+ */
+int
+exec_file (process_rq p)
+{
+ int pid, to_script[2], from_script[2], len, i;
+ io_handle to_io, from_io;
+ const char *content_length;
+
+ content_length
+ = http_request_get_header (p->http_request, "Content-Length");
+
+ /* Set up two pipes between us and the script, one for reading, one
+ * for writing.
+ */
+ if (pipe (to_script) == -1 || pipe (from_script) == -1)
+ return bad_request_error (p, "cannot create pipes to script");
+
+ /* Fork off a process to run the request. */
+ pid = fork ();
+ if (pid == -1)
+ {
+ close (to_script[0]); close (to_script[1]);
+ close (from_script[0]); close (from_script[1]);
+ return bad_request_error (p, "cannot fork");
+ }
+
+ if (pid == 0) /* Child process -- runs the script. */
+ {
+ int j;
+ const char *query_string, *content_type;
+ int major, minor, method;
+ char *header, *env;
+ vector headers;
+ struct sockaddr_in addr;
+ socklen_t addrlen;
+
+ /* XXX Currently all fds will be correctly closed over the exec
+ * except the accepting socket. This requires a small change to
+ * pthrlib to fix. Ignore it for now.
+ */
+ /* Set up fds 0 and 1 to point to the pipes connecting us to
+ * the main rwsd process. Fd 2 points to the error log, so just
+ * leave that one alone.
+ */
+ close (to_script[1]);
+ if (to_script[0] != 0)
+ {
+ dup2 (to_script[0], 0);
+ close (to_script[0]);
+ }
+ close (from_script[0]);
+ if (from_script[1] != 1)
+ {
+ dup2 (from_script[1], 1);
+ close (from_script[1]);
+ }
+
+ /* Query string environment variable. */
+ query_string = http_request_query_string (p->http_request);
+ if (query_string)
+ setenv ("QUERY_STRING", query_string, 1);
+
+ /* Set server protocol. */
+ http_request_version (p->http_request, &major, &minor);
+ setenv ("SERVER_PROTOCOL",
+ psprintf (p->pool, "HTTP/%d.%d", major, minor), 1);
+
+ /* Set request method. */
+ method = http_request_method (p->http_request);
+ setenv ("REQUEST_METHOD",
+ (method == HTTP_METHOD_GET ? "GET" :
+ (method == HTTP_METHOD_POST ? "POST" :
+ (method == HTTP_METHOD_HEAD ? "HEAD" :
+ "unknown"))), 1);
+
+ /* Content length, content type. */
+ if (content_length) setenv ("CONTENT_LENGTH", content_length, 1);
+ content_type
+ = http_request_get_header (p->http_request, "Content-Type");
+ if (content_type) setenv ("CONTENT_TYPE", content_type, 1);
+
+ /* Get peer address. */
+ addrlen = sizeof addr;
+ getpeername (p->sock, (struct sockaddr *) &addr, &addrlen);
+
+ /* General CGI environment variables. */
+ setenv ("SERVER_SOFTWARE", http_get_servername (), 1);
+ setenv ("SERVER_NAME", p->host_header, 1);
+ setenv ("GATEWAY_INTERFACE", "CGI/1.1", 1);
+ /*setenv ("SERVER_PORT", pitoa (p->pool, port), 1); XXX */
+ setenv ("PATH_INFO", p->canonical_path, 1);
+ setenv ("PATH_TRANSLATED", p->file_path, 1);
+ setenv ("SCRIPT_NAME", p->canonical_path, 1);
+ setenv ("REMOTE_ADDR", inet_ntoa (addr.sin_addr), 1);
+
+ /* Convert any other headers into HTTP_* environment variables. */
+ headers = http_request_get_headers (p->http_request);
+ for (i = 0; i < vector_size (headers); ++i)
+ {
+ vector_get (headers, i, header);
+ env = pstrdup (p->pool, header);
+ pstrupr (env);
+ for (j = 0; j < strlen (env); ++j)
+ if (env[j] == '-') env[j] = '_';
+ env = psprintf (p->pool, "HTTP_%s", env);
+ setenv (env, http_request_get_header (p->http_request, header), 1);
+ }
+
+ /* Run the CGI script. */
+ execl (p->file_path, p->file_path, 0);
+
+ perror ("exec");
+ exit (1);
+ }
+
+ /* Close the unneeded halves of each pipe. */
+ close (to_script[0]);
+ close (from_script[1]);
+
+ /* Set the ends of the pipes to non-blocking mode. */
+ if (fcntl (to_script[1], F_SETFL, O_NONBLOCK) < 0 ||
+ fcntl (from_script[0], F_SETFL, O_NONBLOCK) < 0)
+ { perror ("fcntl"); exit (1); }
+
+ /* Associate the ends of the pipe with IO handles. This will also
+ * close them automagically in case of error.
+ */
+ to_io = io_fdopen (to_script[1]);
+ from_io = io_fdopen (from_script[0]);
+ if (to_io == 0 || from_io == 0)
+ return bad_request_error (p, "error associating pipes with IO handles");
+
+ /* If this is a POST method, copy the required amount of data
+ * to the CGI script.
+ */
+ if (http_request_method (p->http_request) == HTTP_METHOD_POST)
+ {
+ /* How much to copy? Is content-length set? */
+ len = -1;
+ if (content_length) sscanf (content_length, "%d", &len);
+
+ /* Copy the data to the script. */
+ io_copy (p->io, to_io, len);
+ }
+
+ /* Read data back from the script and out to the client. */
+ io_copy (from_io, p->io, -1);
+
+ /* Force us to close the connection back to the client now. */
+ return 1;
+}
--- /dev/null
+/* CGI scripts
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: exec.h,v 1.1 2001/03/26 15:39:48 rich Exp $
+ */
+
+#ifndef EXEC_H
+#define EXEC_H
+
+#include "config.h"
+
+#include "process_rq.h"
+
+extern int exec_file (process_rq p);
+
+#endif /* EXEC_H */
--- /dev/null
+/* Shared object scripts.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: exec_so.c,v 1.10 2003/01/31 14:36:22 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include <pool.h>
+#include <hash.h>
+
+#include <pthr_pseudothread.h>
+#include <pthr_iolib.h>
+#include <pthr_http.h>
+#include <pthr_cgi.h>
+
+#include "rws_request.h"
+#include "process_rq.h"
+#include "errors.h"
+#include "cfg.h"
+#include "exec_so.h"
+
+/* XXX make+ configure should figure this out. */
+#ifndef __OpenBSD__
+#define HANDLE_REQUEST_SYM "handle_request"
+#else
+#define HANDLE_REQUEST_SYM "_handle_request"
+#endif
+
+static shash cache = 0;
+struct shared_object
+{
+ void *dl_handle; /* Handle returned by dlopen(3) */
+ /* Pointer to 'handle_request' fn. */
+ int (*handle_request) (rws_request rq);
+ time_t mtime; /* Modification time of this file at load. */
+ int use_count; /* Number of current users. */
+};
+
+/* This structure is used when jumping into the handle_request function,
+ * so we can catch errors and return values from this function.
+ */
+struct fn_result
+{
+ struct shared_object *so; /* Parameter to the call. */
+ rws_request rq; /* Parameter to the call. */
+ int close; /* Return value from the call. */
+};
+
+static void call_handle_request (void *data);
+static int do_error (process_rq p, const char *msg);
+
+void
+exec_so_init ()
+{
+ cache = new_shash (global_pool, struct shared_object *);
+}
+
+int
+exec_so_file (process_rq p)
+{
+ struct shared_object *so;
+ const char *error;
+ rws_request rq;
+ struct fn_result fn_result;
+
+ /* Check our cache of currently loaded .so files to see if this one
+ * has already been loaded.
+ */
+ if (!shash_get (cache, p->file_path, so))
+ {
+ /* No: Need to dlopen this file. */
+ so = pmalloc (global_pool, sizeof *so);
+
+ reload:
+ so->dl_handle = dlopen (p->file_path,
+#ifndef __OpenBSD__
+ RTLD_NOW
+#else
+ O_RDWR
+#endif
+ );
+ if (so->dl_handle == 0)
+ {
+ fprintf (stderr, "%s\n", dlerror ());
+ return bad_request_error (p,
+ "failed to load shared object file");
+ }
+
+ /* Check it contains the 'handle_request' function. */
+ so->handle_request = dlsym (so->dl_handle, HANDLE_REQUEST_SYM);
+ if ((error = dlerror ()) != 0)
+ {
+ fprintf (stderr, "%s\n", error);
+ dlclose (so->dl_handle);
+ return bad_request_error (p,
+ "shared object file does not contain "
+ "handle_request function");
+ }
+
+ so->mtime = p->statbuf.st_mtime;
+ so->use_count = 0;
+
+ /* Add it to the cache. */
+ shash_insert (cache, p->file_path, so);
+ }
+
+ /* Check the modification time. We may need to reload this script if it's
+ * changed on disk. But if there are other current users, then we can't
+ * safely unload the library, so don't try (a later request will reload
+ * it when it's quiet anyway).
+ */
+ if (p->statbuf.st_mtime > so->mtime && so->use_count == 0)
+ {
+ shash_erase (cache, p->file_path);
+ dlclose (so->dl_handle);
+ goto reload;
+ }
+
+ /* OK, we're now about to use this file. */
+ so->use_count++;
+
+ /* Generate the rws_request object. */
+ rq = new_rws_request (p->pool,
+ p->http_request,
+ p->io,
+ p->host_header,
+ p->canonical_path,
+ p->file_path,
+ p->host,
+ p->alias,
+ cfg_get_string,
+ cfg_get_int,
+ cfg_get_bool);
+
+ /* Call the 'handle_request' function.
+ * XXX We could pass environment parameters here, but this requires
+ * a change to pthrlib to allow environment variables to be handled
+ * across context switches.
+ */
+ fn_result.so = so;
+ fn_result.rq = rq;
+ error = pth_catch (call_handle_request, &fn_result);
+
+ /* Finished using the file. */
+ so->use_count--;
+
+ if (error)
+ return do_error (p, error);
+
+ return fn_result.close;
+}
+
+static void
+call_handle_request (void *data)
+{
+ struct fn_result *fn_result = (struct fn_result *) data;
+
+ fn_result->close = fn_result->so->handle_request (fn_result->rq);
+}
+
+static int
+do_error (process_rq p, const char *msg)
+{
+ /* XXX In the future, we'd like to extend this function so that
+ * other non-500 errors can be displayed (particularly for 404
+ * Page Not Found errors).
+ */
+ return bad_request_error (p, msg);
+}
--- /dev/null
+/* Shared object scripts.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: exec_so.h,v 1.1 2002/08/21 13:28:31 rich Exp $
+ */
+
+#ifndef EXEC_SO_H
+#define EXEC_SO_H
+
+#include "config.h"
+
+#include "process_rq.h"
+
+extern void exec_so_init (void);
+
+extern int exec_so_file (process_rq p);
+
+#endif /* EXEC_SO_H */
--- /dev/null
+/* File serving.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: file.c,v 1.15 2003/02/05 23:02:51 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+
+#include <pool.h>
+#include <vector.h>
+#include <hash.h>
+#include <pstring.h>
+#include <pre.h>
+
+#include <pthr_pseudothread.h>
+#include <pthr_http.h>
+#include <pthr_iolib.h>
+
+#include "process_rq.h"
+#include "mime_types.h"
+#include "errors.h"
+#include "exec.h"
+#include "exec_so.h"
+#include "cfg.h"
+#include "re.h"
+#include "file.h"
+
+/* XXX This code doesn't deal with the "If-Modified-Since" header
+ * correctly. It is important to get this fixed in the near future.
+ *
+ * Similarly the code should send "Last-Modified" headers.
+ */
+
+struct hash_key
+{
+ dev_t st_dev;
+ ino_t st_ino;
+};
+
+struct file_info
+{
+ struct pool *pool;
+ struct stat statbuf;
+ void *addr;
+};
+
+static pool file_pool = 0;
+
+#define MAX_MMAP_SIZE (10 * 1024 * 1024)
+#define MAX_ENTRIES 100
+#define MAX_SIZE (100 * 1024 * 1024)
+
+static int total_size = 0;
+static int nr_entries = 0;
+
+/* This is the list of files (of type struct file_info) which are
+ * currently memory mapped. It is stored in no particular order and
+ * may contain blank entries (where a file has been unmapped for example).
+ */
+static vector file_list = 0;
+
+/* This is a list of integers indexing into file_list, stored in LRU
+ * order. Element 0 is the oldest, and larger numbered elements are
+ * younger. New entries are pushed onto the back of this list.
+ */
+static vector lru_list = 0;
+
+/* This hash of { device, inode } -> integer maps unique stat information
+ * about files to their offset in the file_list array above.
+ */
+static hash file_hash = 0;
+
+static void invalidate_entry (void *);
+static int quickly_serve_it (process_rq p, const struct file_info *info, const char *mime_type);
+static int slowly_serve_it (process_rq p, int fd, const char *mime_type);
+static void expires_header (process_rq p, http_response http_response);
+
+/* Initialize structures. */
+void
+file_init ()
+{
+ file_pool = new_subpool (global_pool);
+ file_list = new_vector (file_pool, struct file_info);
+ lru_list = new_vector (file_pool, int);
+ file_hash = new_hash (file_pool, struct hash_key, int);
+}
+
+int
+file_serve (process_rq p)
+{
+ vector extv;
+ const char *mime_type = 0;
+ int offset, fd;
+ struct hash_key key;
+ struct file_info info;
+ void *m;
+
+ /* If this file is an executable .so file, and we are allowed to
+ * run .so files from this directory, then it's a shared object
+ * script. Hand it off to exec_so.c to run.
+ */
+ if (cfg_get_bool (p->host, p->alias, "exec so", 0) &&
+ prematch (p->pool, p->remainder, re_so, 0) &&
+ (p->statbuf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
+ return exec_so_file (p);
+
+ /* If this file is executable, and we are allowed to run files from
+ * this directory, then it's a CGI script. Hand it off to exec.c to
+ * run.
+ */
+ if (cfg_get_bool (p->host, p->alias, "exec", 0) &&
+ (p->statbuf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
+ return exec_file (p);
+
+ /* Are we permitted to show files in this directory? */
+ if (!cfg_get_bool (p->host, p->alias, "show", 0))
+ return bad_request_error (p,
+ "you are not permitted to view files "
+ "in this directory");
+
+ /* Map the file's name to its MIME type. */
+ if ((extv = prematch (p->pool, p->remainder, re_ext, 0)) != 0)
+ {
+ char *ext;
+
+ vector_get (extv, 1, ext);
+ mime_type = mime_types_get_type (ext);
+ }
+ if (!mime_type) mime_type = "application/octet-stream"; /* Default. */
+
+ /* Check the hash to see if we know anything about this file already. */
+ memset (&key, 0, sizeof key);
+ key.st_dev = p->statbuf.st_dev;
+ key.st_ino = p->statbuf.st_ino;
+ if (hash_get (file_hash, key, offset))
+ {
+ /* Cache hit ... */
+ vector_get (file_list, offset, info);
+
+ /* ... but has the file on disk changed since we mapped it? */
+ if (info.statbuf.st_mtime == p->statbuf.st_mtime)
+ return quickly_serve_it (p, &info, mime_type);
+ else
+ /* File has changed: invalidate the cache entry. */
+ delete_pool (info.pool);
+ }
+
+ /* Try to open the file. */
+ fd = open (p->file_path, O_RDONLY);
+ if (fd < 0) return file_not_found_error (p);
+
+ /* Set the FD_CLOEXEC flag so that when we fork off CGI scripts, they
+ * won't inherit the file descriptor.
+ */
+ if (fcntl (fd, F_SETFD, FD_CLOEXEC) < 0) { perror ("fcntl"); exit (1); }
+
+ /* If the file's too large, don't mmap it. */
+ if (p->statbuf.st_size > MAX_MMAP_SIZE)
+ return slowly_serve_it (p, fd, mime_type);
+
+ /* Map the file into memory. */
+ m = mmap (0, p->statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (m == MAP_FAILED)
+ return slowly_serve_it (p, fd, mime_type);
+
+ close (fd);
+
+ /* Evict some entries from the cache to make enough room. */
+ while (nr_entries >= MAX_ENTRIES || total_size >= MAX_SIZE)
+ {
+ vector_get (lru_list, 0, offset);
+ vector_get (file_list, offset, info);
+ delete_pool (info.pool);
+ }
+
+ /* Add the entry to the cache. */
+ info.pool = new_subpool (file_pool);
+ info.statbuf = p->statbuf;
+ info.addr = m;
+ nr_entries++;
+ total_size += p->statbuf.st_size;
+
+ for (offset = 0; offset < vector_size (file_list); ++offset)
+ {
+ struct file_info entry;
+
+ vector_get (file_list, offset, entry);
+ if (entry.pool == 0)
+ {
+ vector_replace (file_list, offset, info);
+ goto added_it;
+ }
+ }
+
+ vector_push_back (file_list, info);
+
+ added_it:
+ hash_insert (file_hash, key, offset);
+ vector_push_back (lru_list, offset);
+
+ pool_register_cleanup_fn (info.pool, invalidate_entry, (void *) offset);
+
+ /* Serve it from memory. */
+ return quickly_serve_it (p, &info, mime_type);
+}
+
+static int
+quickly_serve_it (process_rq p, const struct file_info *info,
+ const char *mime_type)
+{
+ http_response http_response;
+ int cl;
+
+ /* Not changed, so it's a real cache hit. */
+ http_response = new_http_response (p->pool, p->http_request, p->io,
+ 200, "OK");
+ http_response_send_headers (http_response,
+ /* Content type. */
+ "Content-Type", mime_type,
+ /* Content length. */
+ "Content-Length", pitoa (p->pool,
+ info->statbuf.st_size),
+ /* End of headers. */
+ NULL);
+ expires_header (p, http_response);
+ cl = http_response_end_headers (http_response);
+
+ if (http_request_is_HEAD (p->http_request)) return cl;
+
+ io_fwrite (info->addr, info->statbuf.st_size, 1, p->io);
+
+ return cl;
+}
+
+static int
+slowly_serve_it (process_rq p, int fd, const char *mime_type)
+{
+ http_response http_response;
+ const int n = 4096;
+ char *buffer = alloca (n);
+ int r, cl;
+
+ /* Cannot memory map this file. Instead fall back to just reading
+ * it and sending it back through the socket.
+ */
+ http_response = new_http_response (p->pool, p->http_request, p->io,
+ 200, "OK");
+ http_response_send_headers (http_response,
+ /* Content type. */
+ "Content-Type", mime_type,
+ /* Content length. */
+ "Content-Length", pitoa (p->pool,
+ p->statbuf.st_size),
+ /* End of headers. */
+ NULL);
+ expires_header (p, http_response);
+ cl = http_response_end_headers (http_response);
+
+ if (http_request_is_HEAD (p->http_request)) return cl;
+
+ while ((r = read (fd, buffer, n)) > 0)
+ {
+ io_fwrite (buffer, r, 1, p->io);
+ }
+
+ if (r < 0)
+ {
+ perror ("read");
+ }
+
+ close (fd);
+
+ return cl;
+}
+
+/* Send the Expires header, if configured. */
+static void
+expires_header (process_rq p, http_response http_response)
+{
+ const char *expires;
+ char pm, unit;
+ int length;
+
+ expires = cfg_get_string (p->host, p->alias, "expires", 0);
+ if (!expires) return;
+
+ /* Parse the configuration string. */
+ if (sscanf (expires, "%c%d%c", &pm, &length, &unit) == 3 &&
+ (pm == '+' || pm == '-') &&
+ length > 0 &&
+ (unit == 's' || unit == 'm' || unit == 'h' ||
+ unit == 'd' || unit == 'y'))
+ {
+ time_t t;
+ struct tm *tm;
+ char header[64];
+
+ time (&t);
+
+ if (pm == '+')
+ {
+ switch (unit)
+ {
+ case 's': t += length; break;
+ case 'm': t += length * 60; break;
+ case 'h': t += length * (60 * 60); break;
+ case 'd': t += length * (60 * 60 * 24); break;
+ case 'y': t += length * (60 * 60 * 24 * 366); break;
+ }
+ }
+ else
+ {
+ switch (unit)
+ {
+ case 's': t -= length; break;
+ case 'm': t -= length * 60; break;
+ case 'h': t -= length * (60 * 60); break;
+ case 'd': t -= length * (60 * 60 * 24); break;
+ case 'y': t -= length * (60 * 60 * 24 * 366); break;
+ }
+ }
+
+ tm = gmtime (&t);
+ strftime (header, sizeof header, "%a, %d %b %Y %H:%M:%S GMT", tm);
+
+ http_response_send_header (http_response, "Expires", header);
+ }
+ else
+ {
+ fprintf (stderr, "file.c: expires_header: cannot parse '%s'\n",
+ expires);
+ }
+}
+
+static void
+invalidate_entry (void *offset_ptr)
+{
+ int offset = (int) offset_ptr;
+ int i, j;
+ struct file_info info;
+ struct hash_key key;
+
+ /* Pull the invalidated entry out of the file_list. */
+ vector_get (file_list, offset, info);
+
+ /* Remove from the file_hash. */
+ memset (&key, 0, sizeof key);
+ key.st_dev = info.statbuf.st_dev;
+ key.st_ino = info.statbuf.st_ino;
+ if (!hash_erase (file_hash, key)) abort ();
+
+ /* Remove from the lru_list. */
+ for (i = 0; i < vector_size (lru_list); ++i)
+ {
+ vector_get (lru_list, i, j);
+
+ if (j == offset)
+ {
+ vector_erase (lru_list, i);
+ goto found_it;
+ }
+ }
+ abort ();
+
+ found_it:
+ /* Unmap the memory. */
+ munmap (info.addr, info.statbuf.st_size);
+
+ /* Invalidate this entry in the file_list. */
+ info.pool = 0;
+ vector_replace (file_list, offset, info);
+
+ /* Update counters. */
+ nr_entries--;
+ total_size -= info.statbuf.st_size;
+}
--- /dev/null
+/* File serving.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: file.h,v 1.4 2001/03/26 15:39:48 rich Exp $
+ */
+
+#ifndef FILE_H
+#define FILE_H
+
+#include "config.h"
+
+#include "process_rq.h"
+
+extern void file_init (void);
+
+extern int file_serve (process_rq p);
+
+#endif /* FILE_H */
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><!-- -*- html -*- -->
+<html>
+ <head>
+ <title>RWS introductory page</title>
+ <style type="text/css">
+ body {
+ background-color: #ffffff;
+ }
+ h1 {
+ font-size: 14pt;
+ font-weight: bold;
+ text-align: left;
+ color: #444400;
+ }
+ h2 {
+ font-size: 12pt;
+ font-weight: bold;
+ text-align: left;
+ color: #444400;
+ }
+ code {
+ color: green;
+ font-weight: bold;
+ }
+ </style>
+ </head>
+ <body bgcolor="#ffffff">
+
+ <h1>Welcome to RWS</h1>
+ <p>
+ Congratulations! RWS is now installed and running on your system.
+ </p>
+
+ <h2>Shared object script demos</h2>
+
+ <p>
+ Unless you have changed the configuration file, the following links
+ should run the shared object script demos from the <code>examples/</code>
+ directory in the source distribution:
+ </p>
+
+ <ul>
+ <li> <a href="/so-bin/hello.so">/so-bin/hello.so</a> </li>
+ <li> <a href="/so-bin/show_params.so?abc=1&def=2">/so-bin/show_params.so?abc=1&def=2</a> </li>
+ </ul>
+
+ <p>
+ These files are in <code>$prefix/share/rws/so-bin</code>.
+ </p>
+
+ <h2>Monolith demos</h2>
+
+ <p>
+ If you have installed
+ <a href="http://www.annexia.org/freeware/monolith/">monolith</a>,
+ then the following links should run the monolith demos:
+ </p>
+
+ <ul>
+ <li> <a href="/so-bin/01_label_and_button.so">/so-bin/01_label_and_button.so</a> </li>
+ <li> <a href="/so-bin/02_toy_calculator.so">/so-bin/02_toy_calculator.so</a> </li>
+ <li> <a href="/so-bin/03_many_toy_calculators.so">/so-bin/03_many_toy_calculators.so</a> </li>
+ <li> <a href="/so-bin/04_animal_vegetable_mineral.so">/so-bin/04_animal_vegetable_mineral.so</a> </li>
+ <li> <a href="/so-bin/05_popup_windows_and_frames.so">/so-bin/05_popup_windows_and_frames.so</a> </li>
+ <li> <a href="/so-bin/06_big_form.so">/so-bin/06_big_form.so</a> </li>
+ <li> <a href="/so-bin/07_toggle_buttons.so">/so-bin/07_toggle_buttons.so</a> </li>
+ <li> <a href="/so-bin/08_menus.so">/so-bin/08_menus.so</a> </li>
+ </ul>
+
+ <p>
+ These files are in <code>$prefix/share/rws/so-bin</code>.
+ </p>
+
+ <hr>
+
+ <p>
+ The current file is <code>$prefix/share/rws/html/index.html</code>, ie.
+ probably one of the following:
+ </p>
+ <ul>
+ <li> <code>/usr/local/share/rws/html/index.html</code> </li>
+ <li> <code>/usr/share/rws/html/index.html</code> </li>
+ </ul>
+ </body>
+</html>
--- /dev/null
+/* RWS main program.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: main.c,v 1.16 2002/11/27 18:45:23 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include <pool.h>
+#include <pstring.h>
+#include <pre.h>
+
+#include <pthr_http.h>
+#include <pthr_server.h>
+
+#include "cfg.h"
+#include "file.h"
+#include "exec_so.h"
+#include "mime_types.h"
+#include "process_rq.h"
+#include "rewrite.h"
+#include "re.h"
+
+static void startup (int argc, char *argv[]);
+static void start_thread (int sock, void *data);
+static void catch_reload_signal (int sig);
+static void catch_quit_signal (int sig);
+static void catch_child_signal (int sig);
+static void reload_config (void);
+
+const char *config_path = "/etc/rws";
+FILE *access_log;
+
+const pcre *re_alias_start,
+ *re_alias_end,
+ *re_begin,
+ *re_conf_line,
+ *re_ext,
+ *re_icon,
+ *re_so,
+ *re_ws,
+ *re_comma;
+
+int
+main (int argc, char *argv[])
+{
+ const char *user, *name, *stderr_file;
+ int c, stack_size;
+ struct sigaction sa;
+ int foreground = 0;
+ int debug = 0;
+
+ /* Initialise various shared regular expressions. */
+ re_alias_start = precomp (global_pool, "^alias[[:space:]]+(.*)$", 0);
+ re_alias_end = precomp (global_pool, "^end[[:space:]]+alias$", 0);
+ re_begin = precomp (global_pool, "^begin[[:space:]]+(.*):?[[:space:]]*$", 0);
+ re_conf_line = precomp (global_pool, "^(.*):[[:space:]]*(.*)?$", 0);
+ re_ext = precomp (global_pool, "\\.([^.]+)$", 0);
+ re_icon = precomp (global_pool,
+ "([^[:space:]]+)[[:space:]]+([0-9]+)x([0-9]+)[[:space:]]+\"(.*)\"", 0);
+ re_so = precomp (global_pool, "\\.so$", 0);
+ re_ws = precomp (global_pool, "[ \t]+", 0);
+ re_comma = precomp (global_pool, "[,;]+", 0);
+
+ while ((c = getopt (argc, argv, "C:p:a:fd")) != -1)
+ {
+ switch (c)
+ {
+ case 'p':
+ /* ignore */
+ break;
+
+ case 'a':
+ /* ignore */
+ break;
+
+ case 'C':
+ config_path = optarg;
+ break;
+
+ case 'f':
+ foreground = 1;
+ break;
+
+ case 'd':
+ debug = 1;
+ break;
+
+ default:
+ fprintf (stderr, "usage: rws [-d] [-f] [-a address] [-p port] [-C configpath]\n");
+ exit (1);
+ }
+ }
+
+ /* Read configuration file. Do this early so we have configuration
+ * data available for other initializations.
+ */
+ reload_config ();
+
+ /* Change the thread stack size? */
+ stack_size = cfg_get_int (0, 0, "stack size", 0);
+ if (stack_size)
+ pseudothread_set_stack_size (stack_size * 1024);
+
+ /* Initialize the file cache. */
+ file_init ();
+
+ /* Initialize the shared object script cache. */
+ exec_so_init ();
+
+ /* Intercept signals. */
+ memset (&sa, 0, sizeof sa);
+ sa.sa_handler = catch_reload_signal;
+ sa.sa_flags = SA_RESTART;
+ sigaction (SIGHUP, &sa, 0);
+
+ sa.sa_handler = catch_quit_signal;
+ sigaction (SIGINT, &sa, 0);
+ sigaction (SIGQUIT, &sa, 0);
+ sigaction (SIGTERM, &sa, 0);
+
+ sa.sa_handler = catch_child_signal;
+ sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
+ sigaction (SIGCHLD, &sa, 0);
+
+ /* ... but ignore SIGPIPE errors. */
+ sa.sa_handler = SIG_IGN;
+ sa.sa_flags = SA_RESTART;
+ sigaction (SIGPIPE, &sa, 0);
+
+ /* Change user on startup. */
+ user = cfg_get_string (0, 0, "user", "nobody");
+ pthr_server_username (user);
+
+ if (foreground)
+ {
+ pthr_server_disable_chdir ();
+ pthr_server_disable_fork ();
+ }
+
+ if (debug)
+ pthr_server_disable_close ();
+ else
+ {
+ /* Errors to error log file. */
+ stderr_file = cfg_get_string (0, 0, "error log", "/tmp/error_log");
+ pthr_server_stderr_file (stderr_file);
+
+ /* Enable stack trace on SIGSEGV. */
+ pthr_server_enable_stack_trace_on_segv ();
+ }
+
+ /* Set server name. */
+ name = psprintf (global_pool,
+ PACKAGE "/" VERSION " %s",
+ http_get_servername ());
+ http_set_servername (name);
+
+ /* Extra startup. */
+ pthr_server_startup_fn (startup);
+
+ /* Start up the server. */
+ pthr_server_main_loop (argc, argv, start_thread);
+
+ exit (0);
+}
+
+static void
+startup (int argc, char *argv[])
+{
+ FILE *access_log;
+
+ /* Open the access log. */
+ access_log
+ = fopen (cfg_get_string (0, 0, "access log", "/tmp/access_log"), "a");
+ if (access_log == 0)
+ {
+ perror ("open: access log");
+ exit (1);
+ }
+ if (fcntl (fileno (access_log), F_SETFD, FD_CLOEXEC) < 0)
+ { perror ("fcntl"); exit (1); }
+
+ http_set_log_file (access_log);
+}
+
+static void
+start_thread (int sock, void *data)
+{
+ (void) new_process_rq (sock);
+}
+
+static void
+catch_reload_signal (int sig)
+{
+ reload_config ();
+}
+
+static void
+catch_quit_signal (int sig)
+{
+ /* Exit gracefully (how!?!) XXX */
+ exit (0);
+}
+
+static void
+catch_child_signal (int sig)
+{
+ /* Clean up the child process. */
+ wait (0);
+}
+
+static void
+reload_config ()
+{
+ /* Reread configuration file. */
+ cfg_reread_config (config_path);
+
+ /* Read /etc/mime.types file. */
+ mime_types_reread_config (cfg_get_string (0, 0, "mime types file",
+ "/etc/mime.types"));
+
+ /* Reset rewrite rules. */
+ rewrite_reset_rules ();
+}
--- /dev/null
+/* MIME types.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: mime_types.c,v 1.3 2002/10/06 11:57:22 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+
+#include <pool.h>
+#include <hash.h>
+#include <pstring.h>
+#include <pre.h>
+
+#include "re.h"
+#include "mime_types.h"
+
+static pool mt_pool = 0;
+static sash mt_map = 0;
+
+void
+mime_types_reread_config (const char *path)
+{
+ FILE *fp;
+ char *line = 0;
+ pool tmp;
+ vector v;
+ int i;
+ char *mt, *ext;
+
+ if (mt_pool) delete_pool (mt_pool);
+ mt_pool = new_subpool (global_pool);
+
+ mt_map = new_sash (mt_pool);
+
+ tmp = new_subpool (mt_pool);
+
+ /* Read the /etc/mime.types file. */
+ fp = fopen (path, "r");
+ if (fp == 0) { perror (path); exit (1); }
+
+ while ((line = pgetlinec (tmp, fp, line)) != 0)
+ {
+ v = pstrresplit (tmp, line, re_ws);
+
+ switch (vector_size (v))
+ {
+ case 0:
+ abort ();
+
+ case 1:
+ break;
+
+ default:
+ vector_get (v, 0, mt);
+ for (i = 1; i < vector_size (v); ++i)
+ {
+ vector_get (v, i, ext);
+ sash_insert (mt_map, ext, mt);
+ }
+ break;
+ }
+ }
+
+ fclose (fp);
+
+ delete_pool (tmp);
+}
+
+const char *
+mime_types_get_type (const char *ext)
+{
+ const char *mt = 0;
+
+ sash_get (mt_map, ext, mt);
+ return mt;
+}
--- /dev/null
+/* MIME types.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: mime_types.h,v 1.1 2001/03/23 17:51:33 rich Exp $
+ */
+
+#ifndef MIME_TYPES_H
+#define MIME_TYPES_H
+
+extern void mime_types_reread_config (const char *file);
+extern const char *mime_types_get_type (const char *ext);
+
+#endif /* MIME_TYPES_H */
--- /dev/null
+/* Request processing thread.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: process_rq.c,v 1.25 2003/03/01 12:11:33 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include <pool.h>
+#include <vector.h>
+#include <hash.h>
+#include <pstring.h>
+
+#include <pthr_pseudothread.h>
+#include <pthr_iolib.h>
+#include <pthr_http.h>
+#include <pthr_cgi.h>
+
+#include "cfg.h"
+#include "file.h"
+#include "dir.h"
+#include "errors.h"
+#include "rewrite.h"
+#include "process_rq.h"
+
+/* Maximum number of requests to service in one thread. This just acts
+ * as a check on the size of the thread pool, preventing it from growing
+ * out of control.
+ */
+#define MAX_REQUESTS_IN_THREAD 30
+
+#define PR_DEBUG 0 /* Set this to enable debugging. */
+
+static void run (void *vp);
+
+process_rq
+new_process_rq (int sock)
+{
+ pool pool;
+ process_rq p;
+
+ pool = new_pool ();
+ p = pmalloc (pool, sizeof *p);
+
+ memset (p, 0, sizeof *p);
+
+ /* Set the FD_CLOEXEC flag so that when we fork off CGI scripts, they
+ * won't inherit the socket.
+ */
+ if (fcntl (sock, F_SETFD, FD_CLOEXEC) < 0) { perror ("fcntl"); exit (1); }
+
+ p->sock = sock;
+ p->pth = new_pseudothread (pool, run, p, "process_rq");
+
+ pth_start (p->pth);
+
+ return p;
+}
+
+#define THREAD_NAME "rws process request thread"
+
+static void
+run (void *vp)
+{
+ process_rq p = (process_rq) vp;
+ int close = 0;
+ int request_timeout;
+ vector path_comps, v;
+ int i, is_dir, nr_requests = 1;
+ const char *location;
+
+ p->pool = pth_get_pool (p->pth);
+ p->io = io_fdopen (p->sock);
+
+ request_timeout = cfg_get_int (0, 0, "request timeout", 60);
+
+ /* Sit in a loop reading HTTP requests. */
+ while (!close && nr_requests <= MAX_REQUESTS_IN_THREAD)
+ {
+ /* Generic name for this thread. */
+ pth_set_name (THREAD_NAME " (idle)");
+
+ /* Count the number of requests serviced in this thread. */
+ nr_requests++;
+
+ /* Timeout requests. */
+ pth_timeout (request_timeout);
+
+ /* Read the request. */
+ p->http_request = new_http_request (p->pool, p->io);
+ if (p->http_request == 0) /* Normal end of file. */
+ break;
+
+ /* Reset timeout. */
+ pth_timeout (0);
+
+ /* Choose the correct configuration file based on the Host: header. */
+ p->host_header = http_request_get_header (p->http_request, "Host");
+ if (p->host_header)
+ {
+ p->host_header = pstrlwr (pstrdup (p->pool, p->host_header));
+
+ if ((p->host = cfg_get_host (p->host_header)) != 0)
+ goto found_host;
+ fprintf (stderr, "unknown virtual host: %s, trying default\n",
+ p->host_header);
+ }
+
+ p->host_header = "default";
+ if ((p->host = cfg_get_host (p->host_header)) == 0)
+ {
+ close = bad_request_error (p, "no \"default\" virtual host!");
+ continue;
+ }
+ found_host:
+
+ /* Get the originally requested path. */
+ p->requested_path = http_request_path (p->http_request);
+ if (!p->requested_path || p->requested_path[0] != '/')
+ {
+ close = bad_request_error (p, "bad pathname");
+ continue;
+ }
+
+ /* Path may contain % sequences. Unescape them. */
+ p->requested_path = cgi_unescape (p->pool, p->requested_path);
+
+ /* If the path ends in a /, then it's a request for a directory.
+ * Record this fact now, because pstrcsplit will forget about the
+ * trailing slash otherwise.
+ */
+ is_dir = p->requested_path[strlen (p->requested_path)-1] == '/';
+
+ /* Split up the path into individual components. */
+ path_comps = pstrcsplit (p->pool, p->requested_path, '/');
+
+ /* Remove "", "." and ".." components. */
+ for (i = 0; i < vector_size (path_comps); ++i)
+ {
+ char *comp;
+
+ vector_get (path_comps, i, comp);
+
+ if (strcmp (comp, "") == 0 || strcmp (comp, ".") == 0)
+ {
+ vector_erase (path_comps, i);
+ i--;
+ }
+ else if (strcmp (comp, "..") == 0)
+ {
+ if (i > 0)
+ {
+ vector_erase_range (path_comps, i-1, i+1);
+ i -= 2;
+ }
+ else
+ {
+ vector_erase (path_comps, i);
+ i--;
+ }
+ }
+ }
+
+ /* Construct the canonical path. Add a trailing slash if the
+ * original request was for a directory.
+ */
+ p->canonical_path = psprintf (p->pool, "/%s",
+ pjoin (p->pool, path_comps, "/"));
+ if (strlen (p->canonical_path) > 1 && is_dir)
+ p->canonical_path = psprintf (p->pool, "%s/", p->canonical_path);
+
+#if PR_DEBUG
+ fprintf (stderr, "canonical path is %s\n", p->canonical_path);
+#endif
+
+ /* Update the name of the thread with the full request URL. */
+ pth_set_name (psprintf (p->pool, THREAD_NAME " http://%s%s",
+ p->host_header,
+ p->canonical_path));
+
+ /* Apply internal and external rewrite rules. */
+ i = apply_rewrites (p, p->canonical_path, &location);
+ if (i == 1) /* External rewrite. */
+ {
+#if PR_DEBUG
+ fprintf (stderr, "external rewrite rule to %s\n", location);
+#endif
+ close = moved_permanently (p, location);
+ continue;
+ }
+ else if (i == 2) /* Internal rewrite. */
+ {
+#if PR_DEBUG
+ fprintf (stderr, "internal rewrite rule to %s\n", location);
+#endif
+
+ /* Update the http_request object with the new path. This also
+ * changes the query string held in this object so that the cgi
+ * library works correctly.
+ */
+ http_request_set_url (p->http_request, location);
+
+ /* Get the path, minus query string. */
+ p->rewritten_path = http_request_path (p->http_request);
+
+ /* Resplit the path. */
+ path_comps = pstrcsplit (p->pool, p->rewritten_path, '/');
+ }
+
+ /* Look for longest matching alias. */
+ for (i = vector_size (path_comps); i >= 0; --i)
+ {
+ if (i > 0)
+ {
+ v = new_subvector (p->pool, path_comps, 0, i);
+ p->aliasname =
+ psprintf (p->pool, "/%s/", pjoin (p->pool, v, "/"));
+ }
+ else
+ p->aliasname = "/";
+
+#if PR_DEBUG
+ fprintf (stderr, "try to find alias matching %s\n", p->aliasname);
+#endif
+
+ if ((p->alias = cfg_get_alias (p->host, p->aliasname)) != 0)
+ goto found_alias;
+ }
+
+#if PR_DEBUG
+ fprintf (stderr, "no matching alias found\n");
+#endif
+
+ /* No alias. */
+ close = file_not_found_error (p);
+ continue;
+
+ found_alias:
+ /* Build up the remainder of the path and the file. */
+ v = new_subvector (p->pool, path_comps, i, vector_size (path_comps));
+ p->remainder = pjoin (p->pool, v, "/");
+
+ /* Find the root path for this alias. */
+ p->root = cfg_get_string (p->host, p->alias, "path", 0);
+ if (p->root == 0)
+ {
+ close = file_not_found_error (p);
+ continue;
+ }
+
+ /* Construct the file path. */
+ p->file_path = psprintf (p->pool, "%s/%s", p->root, p->remainder);
+
+#if PR_DEBUG
+ fprintf (stderr,
+ "rp = %s, cp = %s, rew = %s, "
+ "an = %s, rem = %s, root = %s, fp = %s, qs = %s\n",
+ p->requested_path, p->canonical_path, p->rewritten_path,
+ p->aliasname, p->remainder, p->root,
+ p->file_path,
+ http_request_query_string (p->http_request) ? : "(null)");
+#endif
+
+ /* Find the file to serve and stat it. */
+ if (stat (p->file_path, &p->statbuf) == -1)
+ {
+ close = file_not_found_error (p);
+ continue;
+ }
+
+ /* If it's a directory, but the last component of the name isn't
+ * a "/" character, then we need to add a "/" character and send
+ * a browser redirect back.
+ */
+ if (S_ISDIR (p->statbuf.st_mode) &&
+ p->canonical_path[strlen(p->canonical_path)-1] != '/')
+ {
+ location = psprintf (p->pool, "%s/", p->requested_path);
+ close = moved_permanently (p, location);
+ continue;
+ }
+
+ /* What type of file are we serving? */
+ if (S_ISREG (p->statbuf.st_mode))
+ {
+ close = file_serve (p);
+ continue;
+ }
+ else if (S_ISDIR (p->statbuf.st_mode))
+ {
+ close = dir_serve (p);
+ continue;
+ }
+
+ /* Bad request. */
+ close = bad_request_error (p, "not a regular file or directory");
+ }
+
+ io_fclose (p->io);
+
+ pth_exit ();
+}
--- /dev/null
+/* Request processing thread.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: process_rq.h,v 1.7 2002/09/02 07:50:09 rich Exp $
+ */
+
+#ifndef PROCESS_RQ_H
+#define PROCESS_RQ_H
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#include <pool.h>
+
+#include <pthr_pseudothread.h>
+#include <pthr_http.h>
+#include <pthr_iolib.h>
+
+/* The PROCESS_RQ type is both the pseudothread object which handles
+ * the request, and also the request information structure itself.
+ */
+
+struct process_rq
+{
+ pseudothread pth; /* Pseudothread handle. */
+ pool pool; /* Thread pool for all allocations. */
+ int sock; /* Socket fd. */
+ io_handle io; /* IO handle. */
+ http_request http_request; /* HTTP request object. */
+ const char *host_header; /* Host header or "default". */
+
+ /* These are used to establish context by the cfg (configuration) code. */
+ void *host; /* Host object. */
+ void *alias; /* Alias object. */
+
+ /* The various different paths.
+ *
+ * REQUESTED_PATH is the path as requested by the user (sans query
+ * string). This path is grotty, containing ".", "..", "///", etc. Do
+ * not use it, except perhaps when displaying error messages.
+ *
+ * CANONICAL_PATH is the requested path cleaned up to remove ".", ".."
+ * etc.
+ *
+ * REWRITTEN_PATH is the path after internal rewrite rules have been
+ * applied.
+ *
+ * ALIASNAME is the alias which matches this path. REMAINDER is the
+ * remaining part of the path. Thus (in theory at least), ALIASNAME +
+ * REMAINDER == CANONICAL_PATH.
+ *
+ * ROOT is the document root corresponding to the matching alias. This
+ * corresponds to the actual path of the file on disk. FILE_PATH is
+ * the full path to the actual file on disk. Thus, ROOT + "/" + REMAINDER
+ * == FILE_PATH.
+ *
+ * Directories are always followed by a "/". If a user requests a
+ * directory which isn't followed by a "/" then the path parsing code
+ * transparently issues a 301 (Permanently Moved) browser redirect
+ * including the corrected path.
+ *
+ * Example (no internal rewrite):
+ * REQUESTED_PATH "/cgi-bin/../docs/dir///file.html"
+ * CANONICAL_PATH "/docs/dir/file.html"
+ * REWRITTEN_PATH "/docs/dir/file.html"
+ * ALIASNAME "/docs/"
+ * REMAINDER "dir/file.html"
+ * ROOT "/home/rich/mydocs"
+ * FILE_PATH "/home/rich/mydocs/dir/file.html"
+ *
+ * Example (with internal rewrite):
+ * REQUESTED_PATH "/cgi-bin/../docs/dir///file.html"
+ * CANONICAL_PATH "/docs/dir/file.html"
+ * REWRITTEN_PATH "/newdocs/dir/file.html"
+ * ALIASNAME "/newdocs/"
+ * REMAINDER "dir/file.html"
+ * ROOT "/home/rich/mynewdocs"
+ * FILE_PATH "/home/rich/mynewdocs/dir/file.html"
+ */
+ const char *requested_path;
+ const char *canonical_path;
+ const char *rewritten_path;
+ const char *aliasname;
+ const char *remainder;
+ const char *root;
+ const char *file_path;
+
+ struct stat statbuf; /* Stat of file. */
+};
+
+typedef struct process_rq *process_rq;
+
+extern process_rq new_process_rq (int sock);
+
+/* Define some RFC-compliant dates to represent past and future. */
+#define DISTANT_PAST "Thu, 01 Dec 1994 16:00:00 GMT"
+#define DISTANT_FUTURE "Sun, 01 Dec 2030 16:00:00 GMT"
+
+/* Headers which are sent to defeat caches. */
+#define NO_CACHE_HEADERS "Cache-Control", "must-revalidate", "Expires", DISTANT_PAST, "Pragma", "no-cache"
+
+#define CRLF "\r\n"
+
+#endif /* PROCESS_RQ_H */
--- /dev/null
+/* Various shared regular expressions.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: re.h,v 1.1 2002/10/06 11:57:22 rich Exp $
+ */
+
+#ifndef RE_H
+#define RE_H
+
+#include <pcre.h>
+
+/* Various shared regular expressions. These are actually defined
+ * in main ().
+ */
+extern const pcre *re_alias_start,
+ *re_alias_end,
+ *re_begin,
+ *re_conf_line,
+ *re_ext,
+ *re_icon,
+ *re_so,
+ *re_ws,
+ *re_comma;
+
+#endif /* RE_H */
--- /dev/null
+/* Rewrite rules.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: rewrite.c,v 1.7 2002/10/20 13:09:10 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <pcre.h>
+
+#include <pool.h>
+#include <vector.h>
+#include <hash.h>
+#include <pstring.h>
+#include <pre.h>
+
+#include "cfg.h"
+#include "process_rq.h"
+#include "re.h"
+#include "rewrite.h"
+
+#define RW_DEBUG 0 /* Set this to enable debugging. */
+
+static pool rw_pool = 0;
+
+/* Cache of host -> struct rw *. The null host is stored with key = "". */
+static shash rw_cache;
+
+/* Pre-parsed rules. */
+struct rw
+{
+ vector rules; /* Vector of struct rw_rule. */
+};
+
+/* Each rule. */
+struct rw_rule
+{
+ const char *pattern_text;
+ const pcre *pattern;
+ const char *sub;
+ int flags;
+#define RW_RULE_EXTERNAL 0x0001
+#define RW_RULE_LAST 0x0002
+#define RW_RULE_QSA 0x0004
+};
+
+static struct rw *parse_rules (const char *cfg);
+static const char *append_qs (process_rq p, const char *path);
+
+void
+rewrite_reset_rules ()
+{
+ if (rw_pool) delete_pool (rw_pool);
+ rw_pool = new_subpool (global_pool);
+
+ rw_cache = new_shash (rw_pool, struct rw *);
+}
+
+int
+apply_rewrites (const process_rq p, const char *path, const char **location)
+{
+ struct rw *rw = 0;
+ const char *host = p->host_header ? p->host_header : "";
+ int i, matches = 0, qsa = 0;
+
+#if RW_DEBUG
+ fprintf (stderr, "apply_rewrites: original path = %s\n",
+ p->canonical_path);
+#endif
+
+ /* Get the configuration entry. (Note the alias is not known
+ * yet, so rewrite rules inside alias sections have no effect).
+ */
+ if (!shash_get (rw_cache, host, rw))
+ {
+ const char *cfg;
+
+ cfg = cfg_get_string (p->host, 0, "rewrite", 0);
+ if (cfg) rw = parse_rules (cfg);
+ shash_insert (rw_cache, host, rw);
+ }
+
+ if (!rw) /* No matching rule. */
+ {
+#if RW_DEBUG
+ fprintf (stderr, "apply_rewrites: no matching rule\n");
+#endif
+ return 0;
+ }
+
+ /* Look for a matching rule. */
+ for (i = 0; i < vector_size (rw->rules); ++i)
+ {
+ struct rw_rule rule;
+ const char *old_path = path;
+
+ vector_get (rw->rules, i, rule);
+
+#if RW_DEBUG
+ fprintf (stderr, "apply_rewrites: try matching against %s\n",
+ rule.pattern_text);
+#endif
+
+ path = presubst (p->pool, old_path, rule.pattern, rule.sub, 0);
+ if (path != old_path) /* It matched. */
+ {
+ matches = 1;
+ if (rule.flags & RW_RULE_QSA) qsa = 1;
+
+#if RW_DEBUG
+ fprintf (stderr, "apply_rewrites: it matches %s\n",
+ rule.pattern_text);
+#endif
+
+ /* External link? If so, send a redirect. External rules are
+ * always 'last'.
+ */
+ if (rule.flags & RW_RULE_EXTERNAL)
+ {
+#if RW_DEBUG
+ fprintf (stderr,
+ "apply_rewrites: external: send redirect to %s\n",
+ path);
+#endif
+ *location = qsa ? append_qs (p, path) : path;
+ return 1;
+ }
+
+ /* Last rule? */
+ if (rule.flags & RW_RULE_LAST)
+ {
+#if RW_DEBUG
+ fprintf (stderr,
+ "apply_rewrites: last rule: finished with %s\n",
+ path);
+#endif
+ *location = qsa ? append_qs (p, path) : path;
+ return 2;
+ }
+
+ /* Jump back to the beginning of the list. */
+ i = -1;
+ }
+ }
+
+#if RW_DEBUG
+ fprintf (stderr,
+ "apply_rewrites: finished with %s\n",
+ path);
+#endif
+
+ if (matches)
+ {
+ *location = qsa ? append_qs (p, path) : path;
+ return 2;
+ }
+
+ return 0;
+}
+
+/* Append query string, if there is one. */
+static const char *
+append_qs (process_rq p, const char *path)
+{
+ pool pool = p->pool;
+ const char *qs = http_request_query_string (p->http_request);
+
+ if (qs && strlen (qs) > 0)
+ {
+ const char *t = strchr (path, '?');
+
+ if (t) /* Path already has a query string? */
+ {
+ if (t[1] != '\0')
+ return psprintf (pool, "%s&%s", path, qs);
+ else
+ return psprintf (pool, "%s%s", path, qs);
+ }
+ else /* Path doesn't have a query string. */
+ return psprintf (pool, "%s?%s", path, qs);
+ }
+ return path; /* No query string. */
+}
+
+static void parse_error (const char *line, const char *msg);
+
+static struct rw *
+parse_rules (const char *cfg)
+{
+ pool tmp = new_subpool (rw_pool);
+ vector lines;
+ const char *line;
+ struct rw *rw;
+ struct rw_rule rule;
+ int i;
+
+ /* Split up the configuration string into lines. */
+ lines = pstrcsplit (tmp, cfg, '\n');
+
+ /* Remove any empty lines (these have probably already been removed,
+ * but we can safely do this again anyway).
+ */
+ for (i = 0; i < vector_size (lines); ++i)
+ {
+ vector_get (lines, i, line);
+
+ if (strcmp (line, "") == 0)
+ {
+ vector_erase (lines, i);
+ i--;
+ }
+ }
+
+ if (vector_size (lines) == 0) { delete_pool (tmp); return 0; }
+
+ /* Allocate space for the return structure. */
+ rw = pmalloc (rw_pool, sizeof *rw);
+ rw->rules = new_vector (rw_pool, struct rw_rule);
+
+ /* Each line is a separate rule in the current syntax, so examine
+ * each line and turn it into a rule.
+ */
+ for (i = 0; i < vector_size (lines); ++i)
+ {
+ vector v;
+
+ vector_get (lines, i, line);
+
+ v = pstrresplit (tmp, line, re_ws);
+
+ if (vector_size (v) < 2 || vector_size (v) > 3)
+ parse_error (line, "unrecognised format");
+ vector_get (v, 0, rule.pattern_text);
+ rule.pattern_text = pstrdup (rw_pool, rule.pattern_text);
+ rule.pattern = precomp (rw_pool, rule.pattern_text, 0);
+ vector_get (v, 1, rule.sub);
+ rule.sub = pstrdup (rw_pool, rule.sub);
+
+ /* Parse the flags. */
+ rule.flags = 0;
+ if (vector_size (v) == 3)
+ {
+ const char *flags;
+ int j;
+
+ vector_get (v, 2, flags);
+ v = pstrresplit (tmp, flags, re_comma);
+
+ for (j = 0; j < vector_size (v); ++j)
+ {
+ const char *flag;
+
+ vector_get (v, j, flag);
+
+ if (strcasecmp (flag, "external") == 0)
+ rule.flags |= RW_RULE_EXTERNAL;
+ else if (strcasecmp (flag, "last") == 0)
+ rule.flags |= RW_RULE_LAST;
+ else if (strcasecmp (flag, "qsa") == 0)
+ rule.flags |= RW_RULE_QSA;
+ else
+ parse_error (line, "unknown flag");
+ }
+ }
+
+#if RW_DEBUG
+ fprintf (stderr,
+ "parse rule: pattern=%s sub=%s flags=0x%04x\n",
+ rule.pattern_text, rule.sub, rule.flags);
+#endif
+
+ vector_push_back (rw->rules, rule);
+ }
+
+ delete_pool (tmp);
+ return rw;
+}
+
+static void
+parse_error (const char *line, const char *msg)
+{
+ fprintf (stderr,
+ "rewrite rule: %s\n"
+ "at line: %s\n",
+ msg, line);
+ exit (1);
+}
--- /dev/null
+/* Rewrite rules.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: rewrite.h,v 1.4 2002/10/09 19:13:14 rich Exp $
+ */
+
+#ifndef REWRITE_H
+#define REWRITE_H
+
+#include <pool.h>
+
+#include "process_rq.h"
+
+/* Reset the rewrite rules. */
+extern void rewrite_reset_rules (void);
+
+/* This function applies internal and external rewrite rules found
+ * in the configuration file. If there is no rewrite, *location is
+ * left alone and the function returns 0. If there is an external
+ * rewrite, *location points to the rewritten path and the function
+ * returns 1. Internal rewrites are the same as external rewrites
+ * except the function returns 2.
+ */
+extern int apply_rewrites (const process_rq p, const char *path, const char **location);
+
+#endif /* REWRITE_H */
--- /dev/null
+#!/bin/sh
+#
+# rws This shell script takes care of starting and stopping rws.
+#
+# chkconfig: 2345 80 30
+# description: RWS is a webserver
+# processname: rwsd
+# config: /etc/rws/rws.conf
+# pidfile: /var/run/rws.pid
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+# Source networking configuration.
+. /etc/sysconfig/network
+
+# Check that networking is up.
+[ ${NETWORKING} = "no" ] && exit 0
+
+[ -f /usr/sbin/rwsd ] || exit 0
+
+# See how we were called.
+case "$1" in
+ start)
+ # Start daemons.
+
+ echo -n "Starting rws: "
+ daemon /usr/sbin/rwsd
+ echo
+ touch /var/lock/subsys/rws
+ ;;
+ stop)
+ # Stop daemons.
+ echo -n "Shutting down rws: "
+ killproc rwsd
+ echo
+ rm -f /var/lock/subsys/rws
+ ;;
+ restart)
+ $0 stop
+ $0 start
+ ;;
+ status)
+ status rwsd
+ ;;
+ *)
+ echo "Usage: rws.rc {start|stop|restart|status}"
+ exit 1
+esac
+
+exit 0
+
--- /dev/null
+/* Shared object scripts.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: rws_request.c,v 1.5 2002/12/01 14:58:02 rich Exp $
+ */
+
+#include "config.h"
+
+#include "cfg.h"
+#include "rws_request.h"
+
+struct rws_request
+{
+ http_request http_request;
+ io_handle io;
+ const char *host_header;
+ const char *canonical_path;
+ const char *file_path;
+
+ /* These are used for retrieving configuration information.
+ * XXX These are also a huge hack which will be removed when we
+ * have a decent configuration object type in c2lib.
+ */
+ void *host;
+ void *alias;
+ const char * (*cfg_get_string) (void *, void *, const char *, const char *);
+ int (*cfg_get_int) (void *, void *, const char *, int);
+ int (*cfg_get_bool) (void *, void *, const char *, int);
+};
+
+rws_request
+new_rws_request (pool pool, http_request http_request, io_handle io,
+ const char *host_header, const char *canonical_path,
+ const char *file_path, void *host, void *alias,
+ const char * (*cfg_get_string)
+ (void *, void *, const char *, const char *),
+ int (*cfg_get_int) (void *, void *, const char *, int),
+ int (*cfg_get_bool) (void *, void *, const char *, int))
+{
+ rws_request p = pmalloc (pool, sizeof *p);
+
+ p->http_request = http_request;
+ p->io = io;
+ p->host_header = host_header;
+ p->canonical_path = canonical_path;
+ p->file_path = file_path;
+ p->host = host;
+ p->alias = alias;
+ p->cfg_get_string = cfg_get_string;
+ p->cfg_get_int = cfg_get_int;
+ p->cfg_get_bool = cfg_get_bool;
+
+ return p;
+}
+
+http_request
+rws_request_http_request (rws_request p)
+{
+ return p->http_request;
+}
+
+io_handle
+rws_request_io (rws_request p)
+{
+ return p->io;
+}
+
+const char *
+rws_request_host_header (rws_request p)
+{
+ return p->host_header;
+}
+
+const char *
+rws_request_canonical_path (rws_request p)
+{
+ return p->canonical_path;
+}
+
+const char *
+rws_request_file_path (rws_request p)
+{
+ return p->file_path;
+}
+
+const char *
+rws_request_cfg_get_string (rws_request p,
+ const char *key, const char *default_value)
+{
+ return p->cfg_get_string (p->host, p->alias, key, default_value);
+}
+
+int
+rws_request_cfg_get_int (rws_request p,
+ const char *key, int default_value)
+{
+ return p->cfg_get_int (p->host, p->alias, key, default_value);
+}
+
+int
+rws_request_cfg_get_bool (rws_request p,
+ const char *key, int default_value)
+{
+ return p->cfg_get_bool (p->host, p->alias, key, default_value);
+}
--- /dev/null
+/* RWS request object, passed to shared object scripts.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: rws_request.h,v 1.4 2002/12/01 14:58:02 rich Exp $
+ */
+
+#ifndef RWS_REQUEST_H
+#define RWS_REQUEST_H
+
+#include <pool.h>
+#include <pthr_pseudothread.h>
+#include <pthr_http.h>
+#include <pthr_iolib.h>
+
+struct rws_request;
+typedef struct rws_request *rws_request;
+
+/* This is the private interface to building a new rws_request object. It
+ * is called inside rwsd. Shared object scripts will never need to call
+ * this. Use the public interface below only.
+ */
+extern rws_request new_rws_request (pool, http_request, io_handle, const char *host_header, const char *canonical_path, const char *file_path, void *host, void *alias, const char * (*cfg_get_string) (void *, void *, const char *, const char *), int (*cfg_get_int) (void *, void *, const char *, int), int (*cfg_get_bool) (void *, void *, const char *, int));
+
+/* Function: rws_request_http_request - retrieve fields in rws_request object
+ * Function: rws_request_io
+ * Function: rws_request_host_header
+ * Function: rws_request_canonical_path
+ * Function: rws_request_file_path
+ * Function: rws_request_cfg_get_string
+ * Function: rws_request_cfg_get_int
+ * Function: rws_request_cfg_get_bool
+ *
+ * These functions retrieve the fields in an @code{rws_request} object.
+ * This object is passed to shared object scripts when they are invoked
+ * by rws as:
+ *
+ * @code{int handle_request (rws_request rq)}
+ *
+ * @code{rws_request_http_request} returns the current HTTP request
+ * (see @ref{new_http_request(3)}). To parse the CGI parameters, you
+ * need to call @code{new_cgi} (see @ref{new_cgi(3)}).
+ *
+ * @code{rws_request_io} returns the IO handle connected to the
+ * browser.
+ *
+ * @code{rws_request_host_header} returns the contents of the
+ * HTTP @code{Host:} header, or the string @code{default} if none was given.
+ *
+ * @code{rws_request_canonical_path} returns the canonical path
+ * requested by the browser (after removing @code{..}, @code{//}, etc.),
+ * eg. @code{/so-bin/file.so}.
+ *
+ * @code{rws_request_file_path} returns the actual path to the
+ * SO file being requested, eg. @code{/usr/share/rws/so-bin/file.so}.
+ *
+ * @code{rws_request_cfg_get_string} returns the configuration file
+ * string for @code{key}. If there is no entry in the configuration
+ * file, this returns @code{default_value}.
+ *
+ * @code{rws_request_cfg_get_int} returns the string converted to
+ * an integer.
+ *
+ * @code{rws_request_cfg_get_bool} returns the string converted to
+ * a boolean.
+ *
+ * See also: @ref{new_cgi(3)}, @ref{new_http_response(3)},
+ * @ref{new_http_request(3)}, pthrlib tutorial, rws @code{examples/}
+ * directory.
+ */
+extern http_request rws_request_http_request (rws_request);
+extern io_handle rws_request_io (rws_request);
+extern const char *rws_request_host_header (rws_request);
+extern const char *rws_request_canonical_path (rws_request);
+extern const char *rws_request_file_path (rws_request);
+extern const char *rws_request_cfg_get_string (rws_request, const char *key, const char *default_value);
+extern int rws_request_cfg_get_int (rws_request, const char *key, int default_value);
+extern int rws_request_cfg_get_bool (rws_request, const char *key, int default_value);
+
+#endif /* RWS_REQUEST_H */
--- /dev/null
+.TH rwsd 1 "October 22, 2002" "GNU" "Programmer's Manual"
+
+.SH NAME
+rwsd \- a small, fast web server
+
+.SH SYNOPSIS
+.B rwsd
+[\fI\-p port\fR] [\fI\-C configpath\fR]
+[\fI\-d\fR] [\fI\-f\fR] [\fI\-a address\fR]
+
+.SH DESCRIPTION
+\fBrwsd\fR is a small, fast web server, written in C, but with a
+very comprehensible and straightforward design. It uses a lightweight
+threading library called \fBpthrlib\fR (so it runs as a single process)
+and a set of basic data types for C called \fBc2lib\fR.
+
+.SH "COMMAND LINE OPTIONS"
+.TP
+\fB\-p port\fR
+listen on the given port (default is to listen on port 80)
+.TP
+\fB\-C configpath\fR
+find configuration files under path (default is \fB/etc/rws/\fR)
+.TP
+\fB\-d\fR
+do not send stderr to the \fBerror_log\fR file (used when debugging)
+.TP
+\fB\-f\fR
+do not fork into the background (used when debugging)
+.TP
+\fB\-a address\fR
+bind only to the given interface (default is to bind to all interfaces)
+
+.SH CONFIGURATION
+The server is configured through files in the \fB/etc/rws/\fR
+directory. The main configuration file is \fB/etc/rws/rws.conf\fR
+and virtual hosts are configured using files in
+\fB/etc/rws/hosts/\fR\fIhostname\fR, for each virtual host
+\fIhostname\fR.
+
+
+
+
+
+
+.SH "RUNNING THE SERVER"
+To start the server running, log in as root and do:
+.PP
+.B /usr/sbin/rwsd
+.PP
+To stop the server running, do:
+.PP
+.B killall rwsd
+.PP
+A SysV start up script is also installed by default, if you prefer
+to use that method for starting and stopping the service. Do:
+.PP
+.B /etc/init.d/rws.rc start
+.PP
+or:
+.PP
+.B /etc/init.d/rws.rc stop
+.PP
+\fBrws\fR cannot run from \fBinetd\fR.
+
+.SH "HTML FILES"
+
+.SH "DIRECTORIES"
+
+.SH "CGI SCRIPTS"
+
+.SH "SHARED OBJECT SCRIPTS"
+
+
+.SH FILES
+\fB/etc/rws/rws.conf\fR
+.PP
+\fB/etc/rws/hosts/default\fR
+.PP
+\fB/etc/rws/hosts/\fR\fIhostname\fR
+
+.SH "SEE ALSO"
+\fBnew_pool\fP(3),
+\fBnew_pseudothread\fP(3).
+.PP
+\fBhttp://www.annexia.org/freeware/rws/\fP
+
+.SH AUTHOR
+The primary author is Richard W.M. Jones <\fBrich@annexia.org\fR>.
+
+.SH COPYRIGHT
+Copyright \(co 2000-2002 Richard W.M. Jones <\fBrich@annexia.org\fR>.
+
+.SH LICENSE
+This software is licensed under the terms of the GNU Library General Public
+License (GNU LGPL). Please see the file \fBCOPYING.LIB\fR which
+accompanies the source distribution.
--- /dev/null
+#!/bin/sh -
+#
+# Simple shell script which tests whether rws is basically working.
+# - by Richard W.M. Jones <rich@annexia.org>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the Free
+# Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# $Id: test_rws.sh,v 1.4 2003/02/05 23:02:51 rich Exp $
+
+# A random, hopefully free, port.
+port=14136
+
+# We need either 'wget' or 'nc'.
+wget --help >/dev/null 2>&1
+if [ $? -eq 0 ]; then
+ mode=wget
+else
+ nc -h 2>/dev/null
+ if [ $? -eq 1 ]; then
+ mode=nc
+ else
+ echo "Please install either 'wget' or 'nc'."
+ echo "This test did not run."
+ exit 0
+ fi
+fi
+
+echo "Using $mode to fetch URLs."
+
+tmp=/tmp/rws.$$
+rm -rf $tmp
+mkdir $tmp
+
+# Create the configuration directory.
+mkdir -p $tmp/etc/rws/hosts
+
+cat > $tmp/etc/rws/rws.conf <<EOF
+mime types file: $tmp/etc/mime.types
+error log: $tmp/log/error_log
+access log: $tmp/log/access_log
+icon for application/*: /icons/binary.gif 20x22 "Application"
+icon for application/x-tar: /icons/tar.gif 20x22 "Unix tape archive file"
+icon for application/x-gzip: /icons/compressed.gif 20x22 "Compressed file"
+icon for application/zip: /icons/compressed.gif 20x22 "Compressed file"
+icon for audio/*: /icons/sound1.gif 20x22 "Audio file"
+icon for image/*: /icons/image2.gif 20x22 "Image"
+icon for message/*: /icons/quill.gif 20x22 "Mail message"
+icon for text/*: /icons/text.gif 20x22 "Text file"
+icon for video/*: /icons/movie.gif 20x22 "Video file"
+no type icon: /icons/generic.gif 20x22 "File"
+unknown icon: /icons/unknown.gif 20x22 "Unknown file type"
+directory icon: /icons/dir.gif 20x22 "Directory"
+link icon: /icons/link.gif 20x22 "Symbolic link"
+special icon: /icons/sphere2.gif 20x22 "Special file"
+EOF
+
+cat > $tmp/etc/rws/hosts/default <<EOF
+alias /
+ path: $tmp/html
+ show: 1
+ list: 1
+end alias
+alias /so-bin/
+ path: $tmp/so-bin
+ exec so: 1
+end alias
+alias /cgi-bin/
+ path: $tmp/cgi-bin
+ exec: 1
+end alias
+EOF
+(cd $tmp/etc/rws/hosts; ln -s default localhost:$port)
+
+cat > $tmp/etc/mime.types <<EOF
+text/html html
+EOF
+
+mkdir $tmp/log
+
+# Create the content directory.
+mkdir $tmp/html
+mkdir $tmp/html/files
+
+cat > $tmp/html/index.html <<EOF
+<html>
+<body>
+This is the test page.
+MAGIC-1234
+</body>
+</html>
+EOF
+
+cp *.o $tmp/html/files
+
+# Create the so-bin directory.
+mkdir $tmp/so-bin
+cp examples/show_params.so $tmp/so-bin
+chmod 0755 $tmp/so-bin/show_params.so
+
+# Create the CGI directory
+mkdir $tmp/cgi-bin
+cat > $tmp/cgi-bin/test.sh <<EOF
+#!/bin/sh
+echo "HTTP/1.0 200 OK"
+echo "Content-Type: text/plain"
+echo
+echo "This is the test CGI script"
+echo "MAGIC-4321"
+EOF
+chmod 0755 $tmp/cgi-bin/test.sh
+
+# Try to start up the server.
+./rwsd -p $port -f -a 127.0.0.1 -C $tmp/etc/rws &
+rws_pid=$!; sleep 1
+
+# Did it start up?
+if kill -0 $rws_pid; then :;
+else
+ echo "Server did not start up. Check any preceeding messages."
+ exit 1
+fi
+
+echo "Started rwsd instance."
+
+# Fetch function: fetch (server, port, serverpath, file)
+fetch()
+{
+ server=$1
+ port=$2
+ serverpath=$3
+ file=$4
+
+ echo "Fetching http://$server:$port$serverpath ..."
+
+ if [ $mode = "nc" ]; then
+ nc $server $port > $file <<EOF
+GET $serverpath HTTP/1.0
+User-Agent: test_rws.sh
+
+EOF
+ if [ $? -ne 0 ]; then exit 1; fi
+ else # wget mode
+ wget -q -O $file http://$server:$port$serverpath
+ if [ $? -ne 0 ]; then kill $rws_pid; exit 1; fi
+ fi
+}
+
+# Fetch the test file.
+fetch localhost $port /index.html $tmp/downloaded
+if grep -q MAGIC-1234 $tmp/downloaded; then :;
+else
+ echo "Download of a simple file failed!"
+ echo "Look at $tmp/downloaded for clues."
+ kill $rws_pid
+ exit 1
+fi
+rm $tmp/downloaded
+
+# Fetch the directory listing.
+fetch localhost $port /files/ $tmp/downloaded
+if grep -q main.o $tmp/downloaded; then :;
+else
+ echo "Download of a directory listing failed!"
+ echo "Look at $tmp/downloaded for clues."
+ kill $rws_pid
+ exit 1
+fi
+rm $tmp/downloaded
+
+# Test shared object scripts.
+echo "Testing shared object scripts."
+fetch localhost $port '/so-bin/show_params.so?key=value' $tmp/downloaded
+if grep -q 'This is the show_params shared object script' $tmp/downloaded
+then :;
+else
+ echo "Execution of a shared object script failed!"
+ echo "Look at $tmp/downloaded for clues."
+ kill $rws_pid
+ exit 1
+fi
+rm $tmp/downloaded
+
+# Test CGI scripts.
+echo "Testing CGI scripts."
+fetch localhost $port /cgi-bin/test.sh $tmp/downloaded
+if grep -q MAGIC-4321 $tmp/downloaded; then :;
+else
+ echo "Execution of a CGI script failed!"
+ echo "Look at $tmp/downloaded for clues."
+ kill $rws_pid
+ exit 1
+fi
+rm $tmp/downloaded
+
+echo "Test completed OK."
+
+# Kill the server.
+kill $rws_pid
+
+# Remove the temporary directory.
+rm -rf $tmp
+
+exit 0