From f65557702b12d7957eaf98549abea845976cfc60 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Fri, 25 Apr 2014 11:40:26 +0100 Subject: [PATCH] Add to git. --- .cvsignore | 1 + COPYING.LIB | 481 +++++++++++++++++++++++++++++++++ Makefile+ | 442 ++++++++++++++++++++++++++++++ README | 91 +++++++ TODO | 34 +++ acconfig.h | 11 + cfg.c | 319 ++++++++++++++++++++++ cfg.h | 61 +++++ conf/default.in | 84 ++++++ conf/rws.conf.in | 71 +++++ configure | 66 +++++ dir.c | 386 +++++++++++++++++++++++++++ dir.h | 40 +++ doc/.cvsignore | 0 doc/index.html | 489 ++++++++++++++++++++++++++++++++++ doc/rws_request_canonical_path.3.html | 153 +++++++++++ doc/rws_request_file_path.3.html | 153 +++++++++++ doc/rws_request_host_header.3.html | 153 +++++++++++ doc/rws_request_http_request.3.html | 153 +++++++++++ doc/rws_request_io.3.html | 153 +++++++++++ doc/rws_request_pool.3.html | 153 +++++++++++ doc/rws_request_pth.3.html | 153 +++++++++++ errors.c | 132 +++++++++ errors.h | 36 +++ examples/.cvsignore | 0 examples/hello.c | 48 ++++ examples/show_params.c | 87 ++++++ exec.c | 237 ++++++++++++++++ exec.h | 30 +++ exec_so.c | 195 ++++++++++++++ exec_so.h | 32 +++ file.c | 425 +++++++++++++++++++++++++++++ file.h | 32 +++ html/index.html | 85 ++++++ main.c | 266 ++++++++++++++++++ mime_types.c | 92 +++++++ mime_types.h | 27 ++ process_rq.c | 338 +++++++++++++++++++++++ process_rq.h | 127 +++++++++ re.h | 39 +++ rewrite.c | 307 +++++++++++++++++++++ rewrite.h | 40 +++ rws.rc | 52 ++++ rws_request.c | 119 +++++++++ rws_request.h | 93 +++++++ rwsd.1 | 97 +++++++ test_rws.sh | 215 +++++++++++++++ 47 files changed, 6798 insertions(+) create mode 100644 .cvsignore create mode 100644 COPYING.LIB create mode 100644 Makefile+ create mode 100644 README create mode 100644 TODO create mode 100644 acconfig.h create mode 100644 cfg.c create mode 100644 cfg.h create mode 100644 conf/default.in create mode 100644 conf/rws.conf.in create mode 100755 configure create mode 100644 dir.c create mode 100644 dir.h create mode 100644 doc/.cvsignore create mode 100644 doc/index.html create mode 100644 doc/rws_request_canonical_path.3.html create mode 100644 doc/rws_request_file_path.3.html create mode 100644 doc/rws_request_host_header.3.html create mode 100644 doc/rws_request_http_request.3.html create mode 100644 doc/rws_request_io.3.html create mode 100644 doc/rws_request_pool.3.html create mode 100644 doc/rws_request_pth.3.html create mode 100644 errors.c create mode 100644 errors.h create mode 100644 examples/.cvsignore create mode 100644 examples/hello.c create mode 100644 examples/show_params.c create mode 100644 exec.c create mode 100644 exec.h create mode 100644 exec_so.c create mode 100644 exec_so.h create mode 100644 file.c create mode 100644 file.h create mode 100644 html/index.html create mode 100644 main.c create mode 100644 mime_types.c create mode 100644 mime_types.h create mode 100644 process_rq.c create mode 100644 process_rq.h create mode 100644 re.h create mode 100644 rewrite.c create mode 100644 rewrite.h create mode 100755 rws.rc create mode 100644 rws_request.c create mode 100644 rws_request.h create mode 100644 rwsd.1 create mode 100755 test_rws.sh diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..1964f99 --- /dev/null +++ b/.cvsignore @@ -0,0 +1 @@ +build-* \ No newline at end of file diff --git a/COPYING.LIB b/COPYING.LIB new file mode 100644 index 0000000..eb685a5 --- /dev/null +++ b/COPYING.LIB @@ -0,0 +1,481 @@ + 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. + + 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. + + 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. + + 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. + + 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. + + 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. + + 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. + + 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. + + 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 + + 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. + + + Copyright (C) + + 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. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/Makefile+ b/Makefile+ new file mode 100644 index 0000000..8272b35 --- /dev/null +++ b/Makefile+ @@ -0,0 +1,442 @@ +# -*- 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 + +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 %> + +

$(PACKAGE) - $(SUMMARY)

+ +

+ rws is a small, fast web server written in + C. It uses the pthrlib + threading/server library and the c2lib + library of Perl-like basics for C. These make it + amongst the smallest, fastest and most straightforwardly + written web servers available now. +

+ +

+ As of version 1.0.11, the stripped binary is 27K and + size reports: +

+ +
+   text    data     bss     dec     hex filename
+  24332     828      88   25248    62a0 rwsd
+
+ +

+ Of course, most of the magic is in pthrlib. +

+ +

+ It supports a fairly minimal set of features so far: +

+ +
    +
  • Complies (mostly) with HTTP/1.1. +
  • Serves files and includes an mmap(2) + file cache. +
  • Directory listings. +
  • CGI script execution (NPH scripts only!) +
  • Virtual hosts and aliases. +
  • Shared object scripts: essentially CGI scripts + written in C which are dynamically linked into the + server memory at runtime. Very fast. +
  • Rewrite rules using regular expressions. +
+ +

+ There is extensive documentation and a tutorial here. +

+ +

Download

+ + + + + + + + + + + + + + + + + + + + + + +
File Format Contents
$(PACKAGE)-$(VERSION).tar.gz tar.gz Latest source distribution
$(PACKAGE)-$(VERSION)-1.i686.rpm i686 binary RPM Binary server, header files, man pages + for Red Hat Linux
$(PACKAGE)-$(VERSION)-1.src.rpm source RPM Source files for Red Hat Linux
+ +

+ You must install the latest pthrlib + and c2lib libraries first! +

+ + + +

News

+ +

+Sat Feb 8 17:00:47 GMT 2003: +Ported to Solaris, OpenBSD and FreeBSD (thanks to +Jeremy Sowden +and Richard Baker +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 -ldl to LIBS +(thanks to jeffrey at box-sol.com). Build fixes for RH 7.3. +

+ +

+ Sun Dec 8 16:07:20 GMT 2002: + Enabled debugging and optimisations. Converted to use + make+. Changed to support + changes in the pthrlib API. The + -f option prevents the server from + changing directory as well. More descriptive thread + names. Give idle threads a different name. +

+ +

+ Mon Nov 25 09:31:37 GMT 2002: + Added a symbols file for full symbol resolution in monolith. + Added debian/conffiles so that Debian package + won't splat configuration files. + Changed process_rq 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. +

+ +

+ Sun Nov 17 23:31:32 GMT 2002: Debian packages. Added MSP files. + rws_request_* symbols are now in a separate + library. Added rwsd.1 manual page. RWS now + forces connection close on bad requests. Multiple fixes + to the way directories/subdirectories are handled. exec_so + catches pth_die exceptions and prints a + message. More rewrite fixes. Change the default root + to /var/www. Added postinst script + for Debian. +

+ +

+ Thu Nov 14 15:33:29 GMT 2002: Major checkpoint release + for Monolith. +

+ +

+ Sun Oct 20 14:57:29 BST 2002: 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. +

+ +

+ Tue Oct 15 23:40:42 BST 2002: Multiple bug fixes. +

+ +

+ Sun Oct 13 19:04:16 BST 2002: Added + -a, -d and -f + 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 pthrlib. +

+ +

+ Sun Oct 6 13:00:39 BST 2002: New rewrite + module allows comprehensive URL rewriting. Updated to use + the newest version of c2lib. +

+ +

+ Sat Sep 7 15:51:10 BST 2002: Packages are now + available as i686 binary RPMs and source RPMs. +

+ +

Old news and old versions

+ +

+ Sat Aug 31 17:39:36 BST 2002 +

+ +

+ rws-1.0.0.tar.gz released. + This includes a tutorial. +

+ +

+ Thu Aug 22 13:20:32 BST 2002 +

+ +

+ rws-0.9.6.tar.gz released. + This includes manual pages. +

+ +

+ Thu Aug 22 12:27:16 BST 2002 +

+ +

+ rws-0.9.5.tar.gz 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. +

+ +

+ Wed Aug 21 14:20:12 BST 2002 +

+ +

+ rws-0.9.4.tar.gz released. + Support for shared object scripts. +

+ +

+ Thu Jun 21 23:14:48 BST 2001 +

+ +

+ rws-0.9.2.tar.gz released. + Directory listings are sorted alphabetically. Server + signature is printed at the bottom of directory listings. + Make sure you have c2lib >= 1.2.12. +

+ +

+ Tue May 22 14:22:06 BST 2001 +

+ +

+ rws-0.0.9.tar.gz released. + URL paths are now unescaped correctly. + Make sure you have pthrlib >= 2.0.5. +

+ +

+ Tue May 22 11:37:10 BST 2001 +

+ +

+ rws-0.0.8.tar.gz 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 pthrlib >= 2.0.4. +

+ +

+ Tue Apr 10 16:04:41 BST 2001 +

+ +

+ rws-0.0.7.tar.gz released. + Generates access_log file. Directory + listings have been improved considerably. +

+ +

+ Mon Apr 9 17:34:31 BST 2001 +

+ +

+ rws-0.0.6.tar.gz released. + This is the first public version. +

+ +<% 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 diff --git a/README b/README new file mode 100644 index 0000000..2e10c78 --- /dev/null +++ b/README @@ -0,0 +1,91 @@ +rws - a fast, small and efficient web server written in C +written by Richard Jones + +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/<>/maps and /proc/<>/fd/ + (replace <> 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. diff --git a/TODO b/TODO new file mode 100644 index 0000000..2098617 --- /dev/null +++ b/TODO @@ -0,0 +1,34 @@ +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). diff --git a/acconfig.h b/acconfig.h new file mode 100644 index 0000000..93330ac --- /dev/null +++ b/acconfig.h @@ -0,0 +1,11 @@ +#ifndef RWS_CONFIG_H +#define RWS_CONFIG_H + +@TOP@ + +#undef PACKAGE +#undef VERSION + +@BOTTOM@ + +#endif /* RWS_CONFIG_H */ diff --git a/cfg.c b/cfg.c new file mode 100644 index 0000000..95337d9 --- /dev/null +++ b/cfg.c @@ -0,0 +1,319 @@ +/* Configuration file parsing. + * - by Richard W.M. Jones + * + * 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 +#include + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#ifdef HAVE_DIRENT_H +#include +#endif + +#ifdef HAVE_REGEX_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#include +#include +#include +#include +#include + +#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 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; +} diff --git a/cfg.h b/cfg.h new file mode 100644 index 0000000..c41cbb7 --- /dev/null +++ b/cfg.h @@ -0,0 +1,61 @@ +/* Configuration file parsing. + * - by Richard W.M. Jones + * + * 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 + +/* 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 */ diff --git a/conf/default.in b/conf/default.in new file mode 100644 index 0000000..d26a37f --- /dev/null +++ b/conf/default.in @@ -0,0 +1,84 @@ +# Example ``/etc/rws/hosts/<>'' 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 diff --git a/conf/rws.conf.in b/conf/rws.conf.in new file mode 100644 index 0000000..93cbd40 --- /dev/null +++ b/conf/rws.conf.in @@ -0,0 +1,71 @@ +# 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" diff --git a/configure b/configure new file mode 100755 index 0000000..04d9db0 --- /dev/null +++ b/configure @@ -0,0 +1,66 @@ +#!/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 . +# +# 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 < + * + * 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 + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_DIRENT_H +#include +#endif + +#ifdef HAVE_SYS_SYSLIMITS_H +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include + +#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, + "Directory listing: %s" CRLF + "" CRLF + "

Directory listing: %s

" CRLF + "Go up to parent directory" CRLF + "" 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, + "" CRLF, + icon, icon_alt, icon_width, icon_height, + name, + S_ISDIR (file_statbuf.st_mode) ? "/" : "", + name, + link_field, + size); + } + } + + io_fprintf (p->io, + "
\"%s\"%s %s%s
" CRLF + "
%s
" CRLF + "" 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; +} diff --git a/dir.h b/dir.h new file mode 100644 index 0000000..3c921fa --- /dev/null +++ b/dir.h @@ -0,0 +1,40 @@ +/* Directory serving. + * - by Richard W.M. Jones + * + * 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 +#endif + +#include + +#include +#include +#include + +#include "process_rq.h" + +extern int dir_serve (process_rq p); + +#endif /* DIR_H */ diff --git a/doc/.cvsignore b/doc/.cvsignore new file mode 100644 index 0000000..e69de29 diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 0000000..c266726 --- /dev/null +++ b/doc/index.html @@ -0,0 +1,489 @@ + + + + rws documentation index + + + + +

rws documentation index

+ +

Shared object scripts

+ +

+ Shared object scripts are a possibly unique feature of rws. + 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 fork(2). +

+ +

+ On the other hand, the penalty for speed is security, although + competent C programmers who are using all the features of + c2lib and + pthrlib + 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). +

+ +

The anatomy of a shared object script

+ +

+ A shared object script is a .so + file (in other words, a shared library or DLL). + It should contain a single external symbol called + handle_request, prototyped as: +

+ +
+int handle_request (rws_request rq);
+
+ +

+ The rws_request object is defined in + rws_request.h. +

+ +

+ The first time that any client requests the shared + object script, rws calls dlopen(3) + on the file. As noted in the dlopen(3) + manual page, this will cause _init and any + constructor functions in the file to be run. + Then rws creates the rws_request + object (see below) and calls handle_request. + The shared object script remains loaded in memory + after handle_request has returned, ready + for the next invocation. +

+ +

+ On subsequent invocations, dlopen(3) is + not called, so constructors only run once. +

+ +

+ However, on each invocation, rws 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 dlclose(3) first, which + will cause _fini and destructors in the + library to run, and unloads the library from memory. It + then reopens (dlopen(3)) the new file on + disk, as above. Beware that there are some occasions when + rws actually cannot reload a shared object + script, even though it notices that the file has changed + on disk. rws 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. +

+ +

Configuring rws to recognise shared object scripts

+ +

+ rws will not try to run shared object scripts + unless the exec so 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: +

+ +
+alias /so-bin/
+	path: /usr/share/rws/so-bin
+	exec so: 1
+end alias
+
+ +

+ Make sure that the so-bin directory is only + writable by trusted users, and make sure each shared object + script is executable, mode 0755. +

+ +

+ If you can't make your shared object scripts run, then here + is a checklist before you email me: +

+ +
    +
  • Make sure you have put the above alias section into + the correct host file. +
  • exec so option is set? +
  • Restarted rwsd? +
  • Directory is world readable, executable (mode 0755)? +
  • Shared object script is world readable, executable (mode 0755)? +
  • Any unresolved symbols (ldd -r script.so), apart + from the rws_request_* symbols which will be resolved + when the library is loaded into rws? +
  • Missing handle_request function? +
  • handle_request is exported in the dynamic + symbol table (nm -D script.so)? +
  • Check the contents of your error_log file to see + if any error messages were reported. +
+ +

+ I have quite successfully used gdb on a running + server to debug and diagnose problems in shared object + scripts. However note that by default gdb may + have trouble loading the symbol table for your script. Use + the sharedlibrary script.so + command to load symbols instead. +

+ +

Shared object scripts vs. Monolith applications

+ +

+ If you've been looking at the + Monolith + application framework pages, then you may be confused + about how shared object scripts relate to Monolith. +

+ +

+ 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 mod_perl, and this is done + for similar reasons of efficiency). +

+ +

+ 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 .so + file which happens to be (you guessed it) a shared object script. +

+ +

+ 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 rws: +

+ +
    +
  1. +

    + 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. +

    +

    + This is very much the normal way of writing CGI-based + applications. +

    +
  2. Write a Monolith application. This will probably be + in lots of C files, but will compile down and be linked + into a single .so file (eg. email.so) + which is dropped into the so-bin directory. +
  3. +

    + Write a Monolith email super-widget. This is going + to exist in a shared library called + /usr/lib/libmyemail.so + with a corresponding header file defining the interface + called myemail.h. +

    +

    + 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 email.so + (it'll be very tiny) which is dropped into so-bin. +

    +

    + 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. +

    +
+ +

+ 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. +

+ +

+ Of course, the same rws server can serve + shared object scripts, multiple Monolith applications, + flat files, and directory listings, all at the same time. +

+ +

Tutorial on writing shared object scripts

+ +

+ In this tutorial I will explain how the two shared object + script examples supplied with rws work. You + will also need to have read the tutorials for + c2lib and + pthrlib + which you can find by going to their respective web pages. +

+ +

+ The first example, hello.c is very simple indeed. + It's just a "hello world" program. The program starts by + including rws_request.h: +

+ +
+#include <rws_request.h>
+
+ +

+ Following this is the handle_request + function. This is the function which rws + will call every time a user requests the script: +

+ +
+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;
+}
+
+ +

+ We first extract some fields from the rws_request + object. rws 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 + pthrlib functions + new_http_response, + http_response_send_headers and + http_response_end_headers do this. Note + that we send a Content-Type: text/plain + header. You must always generate a correct + Content-Type header. +

+ +

+ If the original request was a HEAD request, then + the client only wants to see the headers, so we stop here. +

+ +

+ Otherwise we generate our message and return. +

+ +

+ NB. Don't call io_fclose on the I/O handle! If you + really want to force the connection to close, set the + close 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. +

+ +

+ The second example, show_params.c, 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. +

+ +

+ As before, we start by including a few useful headers: +

+ +
+#include <pool.h>
+#include <vector.h>
+#include <pthr_cgi.h>
+
+#include <rws_request.h>
+
+ +

+ The handle_request function starts the same way + as before: +

+ +
+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);
+
+ +

+ Then we define some variables that we're going to use: +

+ +
+  cgi cgi;
+  int close, i;
+  http_response http_response;
+  vector headers, params;
+  struct http_header header;
+  const char *name, *value;
+
+ +

+ The actual job of parsing out the CGI parameters is simplified + because pthrlib contains a CGI library + (similar to Perl's CGI.pm): +

+ +
+  /* Parse CGI parameters. */
+  cgi = new_cgi (pool, http_request, io);
+
+ +

+ The response phase begins by sending the HTTP + headers as before: +

+ +
+  /* 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;
+
+ +

+ Now we print out the actual contents of both the + http_request object and the cgi + object. HTTP headers first: +

+ +
+  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");
+
+ +

+ The full URL (including the query string), the path alone, + the query string: +

+ +
+  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");
+
+ +

+ Finally we print out the CGI parameters from the cgi + object: +

+ +
+  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;
+}
+
+ +

Further examples

+ +

+ That's the end of this tutorial. I hope you enjoyed it. Please + contact the author about corrections or to obtain more information. +

+ +

Links to manual pages

+ + + +
+
Richard Jones
+ + +Last modified: Wed Oct 9 20:02:40 BST 2002 + + + diff --git a/doc/rws_request_canonical_path.3.html b/doc/rws_request_canonical_path.3.html new file mode 100644 index 0000000..917f417 --- /dev/null +++ b/doc/rws_request_canonical_path.3.html @@ -0,0 +1,153 @@ + + + + +rws_request_pool + + + +

rws_request_pool

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+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
+ +

SYNOPSIS

+ + + +
+
#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);
+
+ +

DESCRIPTION

+ + + +
+These functions retrieve the fields in an rws_request +object. This object is passed to shared object scripts when +they are invoked by rws as:
+ + + +
+int handle_request (rws_request rq)
+ + + +
+rws_request_pool returns the per-request pool. If you +wish to store data between requests, then use static +variables, or create your own subpool of global_pool, +or make allocations in _init.
+ + + +
+rws_request_pth returns the currently running thread +handle.
+ + + +
+rws_request_http_request returns the current HTTP +request (see new_http_request(3)). To parse the CGI +parameters, you need to call new_cgi (see +new_cgi(3)).
+ + + +
+rws_request_io returns the IO handle connected to the +browser.
+ + + +
+rws_request_host_header returns the contents of the +HTTP Host: header, or the string default if +none was given.
+ + + +
+rws_request_canonical_path returns the canonical path +requested by the browser (after removing .., +//, etc.), eg. /so-bin/file.so.
+ + + +
+rws_request_file_path returns the actual path to the +SO file being requested, eg. +/usr/share/rws/so-bin/file.so.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+rws-0.9.6
+ +

SEE ALSO

+ + + +
+new_cgi(3), new_http_response(3), +new_http_request(3), pthrlib tutorial, rws +examples/ directory.
+
+ + diff --git a/doc/rws_request_file_path.3.html b/doc/rws_request_file_path.3.html new file mode 100644 index 0000000..917f417 --- /dev/null +++ b/doc/rws_request_file_path.3.html @@ -0,0 +1,153 @@ + + + + +rws_request_pool + + + +

rws_request_pool

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+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
+ +

SYNOPSIS

+ + + +
+
#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);
+
+ +

DESCRIPTION

+ + + +
+These functions retrieve the fields in an rws_request +object. This object is passed to shared object scripts when +they are invoked by rws as:
+ + + +
+int handle_request (rws_request rq)
+ + + +
+rws_request_pool returns the per-request pool. If you +wish to store data between requests, then use static +variables, or create your own subpool of global_pool, +or make allocations in _init.
+ + + +
+rws_request_pth returns the currently running thread +handle.
+ + + +
+rws_request_http_request returns the current HTTP +request (see new_http_request(3)). To parse the CGI +parameters, you need to call new_cgi (see +new_cgi(3)).
+ + + +
+rws_request_io returns the IO handle connected to the +browser.
+ + + +
+rws_request_host_header returns the contents of the +HTTP Host: header, or the string default if +none was given.
+ + + +
+rws_request_canonical_path returns the canonical path +requested by the browser (after removing .., +//, etc.), eg. /so-bin/file.so.
+ + + +
+rws_request_file_path returns the actual path to the +SO file being requested, eg. +/usr/share/rws/so-bin/file.so.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+rws-0.9.6
+ +

SEE ALSO

+ + + +
+new_cgi(3), new_http_response(3), +new_http_request(3), pthrlib tutorial, rws +examples/ directory.
+
+ + diff --git a/doc/rws_request_host_header.3.html b/doc/rws_request_host_header.3.html new file mode 100644 index 0000000..917f417 --- /dev/null +++ b/doc/rws_request_host_header.3.html @@ -0,0 +1,153 @@ + + + + +rws_request_pool + + + +

rws_request_pool

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+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
+ +

SYNOPSIS

+ + + +
+
#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);
+
+ +

DESCRIPTION

+ + + +
+These functions retrieve the fields in an rws_request +object. This object is passed to shared object scripts when +they are invoked by rws as:
+ + + +
+int handle_request (rws_request rq)
+ + + +
+rws_request_pool returns the per-request pool. If you +wish to store data between requests, then use static +variables, or create your own subpool of global_pool, +or make allocations in _init.
+ + + +
+rws_request_pth returns the currently running thread +handle.
+ + + +
+rws_request_http_request returns the current HTTP +request (see new_http_request(3)). To parse the CGI +parameters, you need to call new_cgi (see +new_cgi(3)).
+ + + +
+rws_request_io returns the IO handle connected to the +browser.
+ + + +
+rws_request_host_header returns the contents of the +HTTP Host: header, or the string default if +none was given.
+ + + +
+rws_request_canonical_path returns the canonical path +requested by the browser (after removing .., +//, etc.), eg. /so-bin/file.so.
+ + + +
+rws_request_file_path returns the actual path to the +SO file being requested, eg. +/usr/share/rws/so-bin/file.so.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+rws-0.9.6
+ +

SEE ALSO

+ + + +
+new_cgi(3), new_http_response(3), +new_http_request(3), pthrlib tutorial, rws +examples/ directory.
+
+ + diff --git a/doc/rws_request_http_request.3.html b/doc/rws_request_http_request.3.html new file mode 100644 index 0000000..917f417 --- /dev/null +++ b/doc/rws_request_http_request.3.html @@ -0,0 +1,153 @@ + + + + +rws_request_pool + + + +

rws_request_pool

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+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
+ +

SYNOPSIS

+ + + +
+
#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);
+
+ +

DESCRIPTION

+ + + +
+These functions retrieve the fields in an rws_request +object. This object is passed to shared object scripts when +they are invoked by rws as:
+ + + +
+int handle_request (rws_request rq)
+ + + +
+rws_request_pool returns the per-request pool. If you +wish to store data between requests, then use static +variables, or create your own subpool of global_pool, +or make allocations in _init.
+ + + +
+rws_request_pth returns the currently running thread +handle.
+ + + +
+rws_request_http_request returns the current HTTP +request (see new_http_request(3)). To parse the CGI +parameters, you need to call new_cgi (see +new_cgi(3)).
+ + + +
+rws_request_io returns the IO handle connected to the +browser.
+ + + +
+rws_request_host_header returns the contents of the +HTTP Host: header, or the string default if +none was given.
+ + + +
+rws_request_canonical_path returns the canonical path +requested by the browser (after removing .., +//, etc.), eg. /so-bin/file.so.
+ + + +
+rws_request_file_path returns the actual path to the +SO file being requested, eg. +/usr/share/rws/so-bin/file.so.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+rws-0.9.6
+ +

SEE ALSO

+ + + +
+new_cgi(3), new_http_response(3), +new_http_request(3), pthrlib tutorial, rws +examples/ directory.
+
+ + diff --git a/doc/rws_request_io.3.html b/doc/rws_request_io.3.html new file mode 100644 index 0000000..917f417 --- /dev/null +++ b/doc/rws_request_io.3.html @@ -0,0 +1,153 @@ + + + + +rws_request_pool + + + +

rws_request_pool

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+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
+ +

SYNOPSIS

+ + + +
+
#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);
+
+ +

DESCRIPTION

+ + + +
+These functions retrieve the fields in an rws_request +object. This object is passed to shared object scripts when +they are invoked by rws as:
+ + + +
+int handle_request (rws_request rq)
+ + + +
+rws_request_pool returns the per-request pool. If you +wish to store data between requests, then use static +variables, or create your own subpool of global_pool, +or make allocations in _init.
+ + + +
+rws_request_pth returns the currently running thread +handle.
+ + + +
+rws_request_http_request returns the current HTTP +request (see new_http_request(3)). To parse the CGI +parameters, you need to call new_cgi (see +new_cgi(3)).
+ + + +
+rws_request_io returns the IO handle connected to the +browser.
+ + + +
+rws_request_host_header returns the contents of the +HTTP Host: header, or the string default if +none was given.
+ + + +
+rws_request_canonical_path returns the canonical path +requested by the browser (after removing .., +//, etc.), eg. /so-bin/file.so.
+ + + +
+rws_request_file_path returns the actual path to the +SO file being requested, eg. +/usr/share/rws/so-bin/file.so.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+rws-0.9.6
+ +

SEE ALSO

+ + + +
+new_cgi(3), new_http_response(3), +new_http_request(3), pthrlib tutorial, rws +examples/ directory.
+
+ + diff --git a/doc/rws_request_pool.3.html b/doc/rws_request_pool.3.html new file mode 100644 index 0000000..a4853e6 --- /dev/null +++ b/doc/rws_request_pool.3.html @@ -0,0 +1,153 @@ + + + + +rws_request_pool + + + +

rws_request_pool

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+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
+ +

SYNOPSIS

+ + + +
+
#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);
+
+ +

DESCRIPTION

+ + + +
+These functions retrieve the fields in an rws_request +object. This object is passed to shared object scripts when +they are invoked by rws as:
+ + + +
+int handle_request (rws_request rq)
+ + + +
+rws_request_pool returns the per-request pool. If you +wish to store data between requests, then use static +variables, or create your own subpool of global_pool, +or make allocations in _init.
+ + + +
+rws_request_pth returns the currently running thread +handle.
+ + + +
+rws_request_http_request returns the current HTTP +request (see new_http_request(3)). To parse the CGI +parameters, you need to call new_cgi (see +new_cgi(3)).
+ + + +
+rws_request_io returns the IO handle connected to the +browser.
+ + + +
+rws_request_host_header returns the contents of the +HTTP Host: header, or the string default if +none was given.
+ + + +
+rws_request_canonical_path returns the canonical path +requested by the browser (after removing .., +//, etc.), eg. /so-bin/file.so.
+ + + +
+rws_request_file_path returns the actual path to the +SO file being requested, eg. +/usr/share/rws/so-bin/file.so.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+rws-0.9.6
+ +

SEE ALSO

+ + + +
+new_cgi(3), new_http_response(3), +new_http_request(3), pthrlib tutorial, rws +examples/ directory.
+
+ + diff --git a/doc/rws_request_pth.3.html b/doc/rws_request_pth.3.html new file mode 100644 index 0000000..a4853e6 --- /dev/null +++ b/doc/rws_request_pth.3.html @@ -0,0 +1,153 @@ + + + + +rws_request_pool + + + +

rws_request_pool

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+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
+ +

SYNOPSIS

+ + + +
+
#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);
+
+ +

DESCRIPTION

+ + + +
+These functions retrieve the fields in an rws_request +object. This object is passed to shared object scripts when +they are invoked by rws as:
+ + + +
+int handle_request (rws_request rq)
+ + + +
+rws_request_pool returns the per-request pool. If you +wish to store data between requests, then use static +variables, or create your own subpool of global_pool, +or make allocations in _init.
+ + + +
+rws_request_pth returns the currently running thread +handle.
+ + + +
+rws_request_http_request returns the current HTTP +request (see new_http_request(3)). To parse the CGI +parameters, you need to call new_cgi (see +new_cgi(3)).
+ + + +
+rws_request_io returns the IO handle connected to the +browser.
+ + + +
+rws_request_host_header returns the contents of the +HTTP Host: header, or the string default if +none was given.
+ + + +
+rws_request_canonical_path returns the canonical path +requested by the browser (after removing .., +//, etc.), eg. /so-bin/file.so.
+ + + +
+rws_request_file_path returns the actual path to the +SO file being requested, eg. +/usr/share/rws/so-bin/file.so.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+rws-0.9.6
+ +

SEE ALSO

+ + + +
+new_cgi(3), new_http_response(3), +new_http_request(3), pthrlib tutorial, rws +examples/ directory.
+
+ + diff --git a/errors.c b/errors.c new file mode 100644 index 0000000..aa1437e --- /dev/null +++ b/errors.c @@ -0,0 +1,132 @@ +/* Deliver errors back to the user. + * - by Richard W.M. Jones + * + * 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 + +#include +#include +#include + +#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, + "Internal server error" CRLF + "" CRLF + "

500 Internal server error

" CRLF + "There was an error serving this request:" CRLF + "
" CRLF
+	      "%s" CRLF
+	      "
" CRLF + "
" CRLF + "
%s
" CRLF + "" 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, + "File or directory not found" CRLF + "" CRLF + "

404 File or directory not found

" CRLF + "The file you requested was not found on this server." CRLF + "
" CRLF + "
%s
" CRLF + "" 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; +} diff --git a/errors.h b/errors.h new file mode 100644 index 0000000..069cc8b --- /dev/null +++ b/errors.h @@ -0,0 +1,36 @@ +/* Deliver errors back to the user. + * - by Richard W.M. Jones + * + * 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 + +#include +#include +#include + +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 */ diff --git a/examples/.cvsignore b/examples/.cvsignore new file mode 100644 index 0000000..e69de29 diff --git a/examples/hello.c b/examples/hello.c new file mode 100644 index 0000000..52ffbea --- /dev/null +++ b/examples/hello.c @@ -0,0 +1,48 @@ +/* Simplest possible example of a shared object script. + * - by Richard W.M. Jones + * + * 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; +} diff --git a/examples/show_params.c b/examples/show_params.c new file mode 100644 index 0000000..418e6ce --- /dev/null +++ b/examples/show_params.c @@ -0,0 +1,87 @@ +/* More complex example shared object script showing parameter parsing. + * - by Richard W.M. Jones + * + * 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 +#include +#include + +#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; +} diff --git a/exec.c b/exec.c new file mode 100644 index 0000000..0c25c50 --- /dev/null +++ b/exec.c @@ -0,0 +1,237 @@ +/* CGI scripts + * - by Richard W.M. Jones + * + * 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 +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#ifdef HAVE_NETINET_IN_H +#include +#endif + +#ifdef HAVE_ARPA_INET_H +#include +#endif + +#include +#include + +#include + +#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; +} diff --git a/exec.h b/exec.h new file mode 100644 index 0000000..56acb3c --- /dev/null +++ b/exec.h @@ -0,0 +1,30 @@ +/* CGI scripts + * - by Richard W.M. Jones + * + * 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 */ diff --git a/exec_so.c b/exec_so.c new file mode 100644 index 0000000..e4c93ba --- /dev/null +++ b/exec_so.c @@ -0,0 +1,195 @@ +/* Shared object scripts. + * - by Richard W.M. Jones + * + * 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 +#include + +#ifdef HAVE_DLFCN_H +#include +#endif + +#ifdef HAVE_FCNTL_H +#include +#endif + +#include +#include + +#include +#include +#include +#include + +#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); +} diff --git a/exec_so.h b/exec_so.h new file mode 100644 index 0000000..3c57633 --- /dev/null +++ b/exec_so.h @@ -0,0 +1,32 @@ +/* Shared object scripts. + * - by Richard W.M. Jones + * + * 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 */ diff --git a/file.c b/file.c new file mode 100644 index 0000000..fdcc0e0 --- /dev/null +++ b/file.c @@ -0,0 +1,425 @@ +/* File serving. + * - by Richard W.M. Jones + * + * 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 +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_SYS_MMAN_H +#include +#endif + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_TIME_H +#include +#endif + +#ifdef HAVE_ALLOCA_H +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include + +#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; +} diff --git a/file.h b/file.h new file mode 100644 index 0000000..4f08000 --- /dev/null +++ b/file.h @@ -0,0 +1,32 @@ +/* File serving. + * - by Richard W.M. Jones + * + * 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 */ diff --git a/html/index.html b/html/index.html new file mode 100644 index 0000000..ac10674 --- /dev/null +++ b/html/index.html @@ -0,0 +1,85 @@ + + + + RWS introductory page + + + + +

Welcome to RWS

+

+ Congratulations! RWS is now installed and running on your system. +

+ +

Shared object script demos

+ +

+ Unless you have changed the configuration file, the following links + should run the shared object script demos from the examples/ + directory in the source distribution: +

+ + + +

+ These files are in $prefix/share/rws/so-bin. +

+ +

Monolith demos

+ +

+ If you have installed + monolith, + then the following links should run the monolith demos: +

+ + + +

+ These files are in $prefix/share/rws/so-bin. +

+ +
+ +

+ The current file is $prefix/share/rws/html/index.html, ie. + probably one of the following: +

+
    +
  • /usr/local/share/rws/html/index.html
  • +
  • /usr/share/rws/html/index.html
  • +
+ + diff --git a/main.c b/main.c new file mode 100644 index 0000000..27db3c8 --- /dev/null +++ b/main.c @@ -0,0 +1,266 @@ +/* RWS main program. + * - by Richard W.M. Jones + * + * 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 +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_SIGNAL_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#ifdef HAVE_SYS_WAIT_H +#include +#endif + +#ifdef HAVE_FCNTL_H +#include +#endif + +#include +#include +#include + +#include +#include + +#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 (); +} diff --git a/mime_types.c b/mime_types.c new file mode 100644 index 0000000..91a08e2 --- /dev/null +++ b/mime_types.c @@ -0,0 +1,92 @@ +/* MIME types. + * - by Richard W.M. Jones + * + * 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 + +#include +#include +#include +#include + +#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; +} diff --git a/mime_types.h b/mime_types.h new file mode 100644 index 0000000..2fa6b62 --- /dev/null +++ b/mime_types.h @@ -0,0 +1,27 @@ +/* MIME types. + * - by Richard W.M. Jones + * + * 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 */ diff --git a/process_rq.c b/process_rq.c new file mode 100644 index 0000000..a04ae91 --- /dev/null +++ b/process_rq.c @@ -0,0 +1,338 @@ +/* Request processing thread. + * - by Richard W.M. Jones + * + * 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 + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_FCNTL_H +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 (); +} diff --git a/process_rq.h b/process_rq.h new file mode 100644 index 0000000..453ac04 --- /dev/null +++ b/process_rq.h @@ -0,0 +1,127 @@ +/* Request processing thread. + * - by Richard W.M. Jones + * + * 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 +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#include + +#include +#include +#include + +/* 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 */ diff --git a/re.h b/re.h new file mode 100644 index 0000000..d4c1f56 --- /dev/null +++ b/re.h @@ -0,0 +1,39 @@ +/* Various shared regular expressions. + * - by Richard W.M. Jones + * + * 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 + +/* 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 */ diff --git a/rewrite.c b/rewrite.c new file mode 100644 index 0000000..f2de137 --- /dev/null +++ b/rewrite.c @@ -0,0 +1,307 @@ +/* Rewrite rules. + * - by Richard W.M. Jones + * + * 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 + +#ifdef HAVE_STRING_H +#include +#endif + +#include + +#include +#include +#include +#include +#include + +#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); +} diff --git a/rewrite.h b/rewrite.h new file mode 100644 index 0000000..8129ce9 --- /dev/null +++ b/rewrite.h @@ -0,0 +1,40 @@ +/* Rewrite rules. + * - by Richard W.M. Jones + * + * 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 + +#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 */ diff --git a/rws.rc b/rws.rc new file mode 100755 index 0000000..fa46754 --- /dev/null +++ b/rws.rc @@ -0,0 +1,52 @@ +#!/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 + diff --git a/rws_request.c b/rws_request.c new file mode 100644 index 0000000..4ed9ec2 --- /dev/null +++ b/rws_request.c @@ -0,0 +1,119 @@ +/* Shared object scripts. + * - by Richard W.M. Jones + * + * 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); +} diff --git a/rws_request.h b/rws_request.h new file mode 100644 index 0000000..f0ae49a --- /dev/null +++ b/rws_request.h @@ -0,0 +1,93 @@ +/* RWS request object, passed to shared object scripts. + * - by Richard W.M. Jones + * + * 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 +#include +#include +#include + +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 */ diff --git a/rwsd.1 b/rwsd.1 new file mode 100644 index 0000000..2cd8e32 --- /dev/null +++ b/rwsd.1 @@ -0,0 +1,97 @@ +.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. diff --git a/test_rws.sh b/test_rws.sh new file mode 100755 index 0000000..16e2f3d --- /dev/null +++ b/test_rws.sh @@ -0,0 +1,215 @@ +#!/bin/sh - +# +# Simple shell script which tests whether rws is basically working. +# - by Richard W.M. Jones +# +# 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 < $tmp/etc/rws/hosts/default < $tmp/etc/mime.types < $tmp/html/index.html < + +This is the test page. +MAGIC-1234 + + +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 < $file <