base
This commit is contained in:
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 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.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, 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 software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, 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 redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
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 give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
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 Program or any portion
|
||||
of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
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 Program, 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 Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) 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; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, 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 executable. 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.
|
||||
|
||||
If distribution of executable or 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 counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program 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.
|
||||
|
||||
5. 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 Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program 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.
|
||||
|
||||
7. 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 Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program 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 Program.
|
||||
|
||||
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.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program 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.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the 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 Program
|
||||
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 Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, 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
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), 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 Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) 19yy <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; 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.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) 19yy name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,222 @@
|
||||
# Makefile for neon test suite.
|
||||
|
||||
SHELL = /bin/sh
|
||||
CPPFLAGS = -I/usr/local/include -I${top_builddir} -I. -I$(top_srcdir)/src -I$(top_srcdir)/test/common
|
||||
CFLAGS = -g -O2
|
||||
LDFLAGS = -L/usr/local/lib
|
||||
DEFS = -DHAVE_CONFIG_H
|
||||
|
||||
top_builddir = ..
|
||||
top_srcdir = ..
|
||||
srcdir = .
|
||||
|
||||
|
||||
AR = ar
|
||||
|
||||
RANLIB = /usr/bin/ranlib
|
||||
LIBS = $(LIBTEST)
|
||||
CC = gcc
|
||||
OPENSSL =
|
||||
|
||||
HELPERS = $(ZLIB_HELPERS)
|
||||
BASIC_TESTS = uri-tests util-tests string-tests socket \
|
||||
session request auth basic stubs redirect
|
||||
ZLIB_TESTS = compress
|
||||
ZLIB_HELPERS = file1.gz file2.gz trailing.gz badcsum.gz truncated.gz \
|
||||
corrupt1.gz corrupt2.gz empty.gz random.txt
|
||||
DAV_TESTS = xml xmlreq acl props lock
|
||||
SSL_TESTS = socket-ssl ssl
|
||||
SSL_HELPERS = ca-stamp
|
||||
TESTS = $(BASIC_TESTS) $(ZLIB_TESTS) $(DAV_TESTS)
|
||||
VALGRIND = valgrind --tool=memcheck --leak-check=yes --trace-children=no --show-reachable=yes
|
||||
|
||||
# Make every object depend on libneon.la to force a rebuild on any src/* changes
|
||||
OBJDEPS = $(srcdir)/common/tests.h $(srcdir)/common/child.h $(srcdir)/utils.h \
|
||||
$(top_builddir)/config.h $(top_builddir)/src/libneon.la
|
||||
# Test program just depends on libtest
|
||||
DEPS = $(LIBTEST)
|
||||
|
||||
LIBTEST = libtest.la
|
||||
LIBNEON = $(top_builddir)/src/libneon.la
|
||||
|
||||
LIBTOOL = $(SHELL) $(top_builddir)/libtool --silent
|
||||
TEST_LDFLAGS = -no-install
|
||||
LINK = $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) $(TEST_LDFLAGS)
|
||||
COMPILE = $(LIBTOOL) --mode=compile $(CC) $(CPPFLAGS) $(CFLAGS)
|
||||
|
||||
prefix = /usr/local
|
||||
exec_prefix = ${prefix}
|
||||
libdir = ${exec_prefix}/lib
|
||||
|
||||
INSTALL_PROGRAM = ${INSTALL}
|
||||
INSTALL_DATA = ${INSTALL} -m 644
|
||||
INSTALL_SCRIPT = ${INSTALL}
|
||||
INSTALL = /usr/bin/install -c
|
||||
|
||||
INSTALL_HELPERS = $(HELPERS) server.key $(srcdir)/makekeys.sh \
|
||||
$(srcdir)/openssl.conf $(srcdir)/notvalid.pem \
|
||||
$(srcdir)/expired.pem
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .lo .c
|
||||
|
||||
# By default, compile but don't run the tests.
|
||||
all: $(TESTS)
|
||||
|
||||
clean:
|
||||
rm -f $(TESTS) $(HELPERS) *.*o common/*.*o libtest.*a *.log
|
||||
rm -f *.gc* *.da *.bb* common/*.bb* common/*.gc* common/*.da
|
||||
rm -rf ca .libs
|
||||
rm -f ca-stamp client.key *.csr ssigned.pem wrongcn.pem \
|
||||
server.cert client.cert client.p12 *.cert sparse.bin
|
||||
|
||||
check: $(TESTS) $(HELPERS)
|
||||
@SRCDIR=$(srcdir) $(SHELL) $(srcdir)/run.sh $(TESTS)
|
||||
|
||||
grind: $(TESTS) $(HELPERS)
|
||||
@SRCDIR=$(srcdir) HARNESS="$(VALGRIND)" $(SHELL) $(srcdir)/run.sh $(TESTS)
|
||||
|
||||
lfs-check: largefile $(LFS_HELPERS)
|
||||
@SRCDIR=$(srcdir) $(SHELL) $(srcdir)/run.sh largefile
|
||||
|
||||
install: $(TESTS) $(HELPERS)
|
||||
@$(INSTALL) -d $(DESTDIR)$(libdir)/neon-tests
|
||||
@$(INSTALL) -d $(DESTDIR)$(libdir)/neon-tests/bin
|
||||
@$(INSTALL) -d $(DESTDIR)$(libdir)/neon-tests/data
|
||||
@for t in $(TESTS); do \
|
||||
$(LIBTOOL) --mode=install \
|
||||
$(INSTALL_PROGRAM) $$t $(DESTDIR)$(libdir)/neon-tests/bin; \
|
||||
done
|
||||
@for h in $(INSTALL_HELPERS); do \
|
||||
$(INSTALL_DATA) $$h $(DESTDIR)$(libdir)/neon-tests/data; \
|
||||
done
|
||||
@$(INSTALL_PROGRAM) $(srcdir)/run-tests.sh $(DESTDIR)$(libdir)/neon-tests
|
||||
|
||||
NEWS = $(top_srcdir)/NEWS
|
||||
|
||||
file1.gz: $(NEWS)
|
||||
gzip -c --no-name $(NEWS) > $@
|
||||
|
||||
file2.gz: $(NEWS)
|
||||
gzip -c --name $(NEWS) > $@
|
||||
|
||||
# gzip file with trailing bytes.
|
||||
trailing.gz: $(NEWS)
|
||||
gzip -c --no-name $(NEWS) > $@
|
||||
echo "hello, world" >> $@
|
||||
|
||||
truncated.gz: file1.gz
|
||||
dd if=file1.gz of=$@ bs=2048 count=2
|
||||
|
||||
badcsum.gz: file1.gz
|
||||
dd of=$@ if=file1.gz bs=1 count=`perl -e 'printf "%d", (stat("file1.gz"))[7] - 8;'`
|
||||
echo 'broken!' >> $@
|
||||
|
||||
corrupt1.gz: file1.gz
|
||||
dd of=$@ if=file1.gz bs=1 count=500
|
||||
cat $(NEWS) >> $@
|
||||
|
||||
corrupt2.gz:
|
||||
cat $(NEWS) > $@
|
||||
|
||||
empty.gz:
|
||||
touch $@
|
||||
|
||||
random.txt:
|
||||
cat $(NEWS) > $@
|
||||
|
||||
# Dummy target to create the CA keys etc. makekeys stderr is redirected
|
||||
# since it changes for every invocation; not helpful for regression
|
||||
# testing.
|
||||
ca-stamp: $(srcdir)/makekeys.sh $(srcdir)/openssl.conf
|
||||
rm -rf ca
|
||||
OPENSSL=$(OPENSSL) \
|
||||
$(SHELL) $(srcdir)/makekeys.sh $(srcdir) 2>makekeys.out
|
||||
@echo timestamp > ca-stamp
|
||||
|
||||
Makefile: $(srcdir)/Makefile.in
|
||||
cd .. && ./config.status test/Makefile
|
||||
|
||||
LIBOBJS = common/tests.lo common/child.lo utils.lo
|
||||
|
||||
$(LIBTEST): $(LIBOBJS)
|
||||
$(LINK) -o $(LIBTEST) $(LIBOBJS) $(LIBNEON)
|
||||
|
||||
.c.lo:
|
||||
$(COMPILE) -c $< -o $@
|
||||
|
||||
# Recompile socket.c with SOCKET_SSL defined
|
||||
socket-ssl.lo: $(srcdir)/socket.c $(HDRS)
|
||||
$(COMPILE) -DSOCKET_SSL -c $(srcdir)/socket.c -o $@
|
||||
|
||||
socket-ssl: socket-ssl.lo $(LIBTEST)
|
||||
$(LINK) -o $@ socket-ssl.lo $(LIBS)
|
||||
|
||||
resolve: resolve.lo $(LIBNEON)
|
||||
$(LINK) -o $@ resolve.lo $(LIBNEON)
|
||||
|
||||
common/tests.lo: $(srcdir)/common/tests.c $(OBJDEPS)
|
||||
common/child.lo: $(srcdir)/common/child.c $(OBJDEPS)
|
||||
utils.lo: $(srcdir)/utils.c $(OBJDEPS)
|
||||
auth.lo: $(srcdir)/auth.c $(OBJDEPS)
|
||||
uri-tests.lo: $(srcdir)/uri-tests.c $(OBJDEPS)
|
||||
util-tests.lo: $(srcdir)/util-tests.c $(OBJDEPS)
|
||||
string-tests.lo: $(srcdir)/string-tests.c $(OBJDEPS)
|
||||
socket.lo: $(srcdir)/socket.c $(OBJDEPS)
|
||||
server.lo: $(srcdir)/server.c $(OBJDEPS)
|
||||
request.lo: $(srcdir)/request.c $(OBJDEPS)
|
||||
regress.lo: $(srcdir)/regress.c $(OBJDEPS)
|
||||
compress.lo: $(srcdir)/compress.c $(OBJDEPS)
|
||||
acl.lo: $(srcdir)/acl.c $(OBJDEPS)
|
||||
utils.lo: $(srcdir)/utils.c $(OBJDEPS)
|
||||
stubs.lo: $(srcdir)/stubs.c $(OBJDEPS)
|
||||
props.lo: $(srcdir)/props.c $(OBJDEPS)
|
||||
session.lo: $(srcdir)/session.c $(OBJDEPS)
|
||||
redirect.lo: $(srcdir)/redirect.c $(OBJDEPS)
|
||||
basic.lo: $(srcdir)/basic.c $(OBJDEPS)
|
||||
ssl.lo: $(srcdir)/ssl.c $(OBJDEPS)
|
||||
lock.lo: $(srcdir)/lock.c $(OBJDEPS)
|
||||
largefile.lo: $(srcdir)/largefile.c $(OBJDEPS)
|
||||
|
||||
auth: auth.lo $(DEPS)
|
||||
$(LINK) -o $@ auth.lo $(DEPS)
|
||||
basic: basic.lo $(DEPS)
|
||||
$(LINK) -o $@ basic.lo $(DEPS)
|
||||
uri-tests: uri-tests.lo $(DEPS)
|
||||
$(LINK) -o $@ uri-tests.lo $(DEPS)
|
||||
util-tests: util-tests.lo $(DEPS)
|
||||
$(LINK) -o $@ util-tests.lo $(DEPS)
|
||||
string-tests: string-tests.lo $(DEPS)
|
||||
$(LINK) -o $@ string-tests.lo $(DEPS)
|
||||
socket: socket.lo $(DEPS)
|
||||
$(LINK) -o $@ socket.lo $(DEPS)
|
||||
server: server.lo $(DEPS)
|
||||
$(LINK) -o $@ server.lo $(DEPS)
|
||||
request: request.lo $(DEPS)
|
||||
$(LINK) -o $@ request.lo $(DEPS)
|
||||
regress: regress.lo $(DEPS)
|
||||
$(LINK) -o $@ regress.lo $(DEPS)
|
||||
compress: compress.lo $(DEPS)
|
||||
$(LINK) -o $@ compress.lo $(DEPS)
|
||||
acl: acl.lo $(DEPS)
|
||||
$(LINK) -o $@ acl.lo $(DEPS)
|
||||
utils: utils.lo $(DEPS)
|
||||
$(LINK) -o $@ utils.lo $(DEPS)
|
||||
stubs: stubs.lo $(DEPS)
|
||||
$(LINK) -o $@ stubs.lo $(DEPS)
|
||||
props: props.lo $(DEPS)
|
||||
$(LINK) -o $@ props.lo $(DEPS)
|
||||
session: session.lo $(DEPS)
|
||||
$(LINK) -o $@ session.lo $(DEPS)
|
||||
redirect: redirect.lo $(DEPS)
|
||||
$(LINK) -o $@ redirect.lo $(DEPS)
|
||||
ssl: ssl.lo $(DEPS)
|
||||
$(LINK) -o $@ ssl.lo $(DEPS)
|
||||
xml: xml.lo $(DEPS)
|
||||
$(LINK) -o $@ xml.lo $(DEPS)
|
||||
xmlreq: xmlreq.lo $(DEPS)
|
||||
$(LINK) -o $@ xmlreq.lo $(DEPS)
|
||||
lock: lock.lo $(DEPS)
|
||||
$(LINK) -o $@ lock.lo $(DEPS)
|
||||
largefile: largefile.lo $(DEPS)
|
||||
$(LINK) -o $@ largefile.lo $(DEPS)
|
||||
@@ -0,0 +1,222 @@
|
||||
# Makefile for neon test suite.
|
||||
|
||||
SHELL = @SHELL@
|
||||
CPPFLAGS = @CPPFLAGS@ -I. -I$(top_srcdir)/src -I$(top_srcdir)/test/common
|
||||
CFLAGS = @CFLAGS@ @NEON_CFLAGS@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
DEFS = @DEFS@
|
||||
|
||||
top_builddir = ..
|
||||
top_srcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
AR = ar
|
||||
|
||||
RANLIB = @RANLIB@
|
||||
LIBS = $(LIBTEST)
|
||||
CC = @CC@
|
||||
OPENSSL = @OPENSSL@
|
||||
|
||||
HELPERS = @HELPERS@
|
||||
BASIC_TESTS = uri-tests util-tests string-tests socket \
|
||||
session request auth basic stubs redirect
|
||||
ZLIB_TESTS = compress
|
||||
ZLIB_HELPERS = file1.gz file2.gz trailing.gz badcsum.gz truncated.gz \
|
||||
corrupt1.gz corrupt2.gz empty.gz random.txt
|
||||
DAV_TESTS = xml xmlreq acl props lock
|
||||
SSL_TESTS = socket-ssl ssl
|
||||
SSL_HELPERS = ca-stamp
|
||||
TESTS = @TESTS@
|
||||
VALGRIND = valgrind --tool=memcheck --leak-check=yes --trace-children=no --show-reachable=yes
|
||||
|
||||
# Make every object depend on libneon.la to force a rebuild on any src/* changes
|
||||
OBJDEPS = $(srcdir)/common/tests.h $(srcdir)/common/child.h $(srcdir)/utils.h \
|
||||
$(top_builddir)/config.h $(top_builddir)/src/libneon.la
|
||||
# Test program just depends on libtest
|
||||
DEPS = $(LIBTEST)
|
||||
|
||||
LIBTEST = libtest.la
|
||||
LIBNEON = $(top_builddir)/src/libneon.la
|
||||
|
||||
LIBTOOL = @LIBTOOL@ --silent
|
||||
TEST_LDFLAGS = @TEST_LDFLAGS@
|
||||
LINK = $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) $(TEST_LDFLAGS)
|
||||
COMPILE = $(LIBTOOL) --mode=compile $(CC) $(CPPFLAGS) $(CFLAGS)
|
||||
|
||||
prefix = @prefix@
|
||||
exec_prefix = @exec_prefix@
|
||||
libdir = @libdir@
|
||||
|
||||
INSTALL_PROGRAM = @INSTALL_PROGRAM@
|
||||
INSTALL_DATA = @INSTALL_DATA@
|
||||
INSTALL_SCRIPT = @INSTALL_SCRIPT@
|
||||
INSTALL = @INSTALL@
|
||||
|
||||
INSTALL_HELPERS = $(HELPERS) server.key $(srcdir)/makekeys.sh \
|
||||
$(srcdir)/openssl.conf $(srcdir)/notvalid.pem \
|
||||
$(srcdir)/expired.pem
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .lo .c
|
||||
|
||||
# By default, compile but don't run the tests.
|
||||
all: $(TESTS)
|
||||
|
||||
clean:
|
||||
rm -f $(TESTS) $(HELPERS) *.*o common/*.*o libtest.*a *.log
|
||||
rm -f *.gc* *.da *.bb* common/*.bb* common/*.gc* common/*.da
|
||||
rm -rf ca .libs
|
||||
rm -f ca-stamp client.key *.csr ssigned.pem wrongcn.pem \
|
||||
server.cert client.cert client.p12 *.cert sparse.bin
|
||||
|
||||
check: $(TESTS) $(HELPERS)
|
||||
@SRCDIR=$(srcdir) $(SHELL) $(srcdir)/run.sh $(TESTS)
|
||||
|
||||
grind: $(TESTS) $(HELPERS)
|
||||
@SRCDIR=$(srcdir) HARNESS="$(VALGRIND)" $(SHELL) $(srcdir)/run.sh $(TESTS)
|
||||
|
||||
lfs-check: largefile $(LFS_HELPERS)
|
||||
@SRCDIR=$(srcdir) $(SHELL) $(srcdir)/run.sh largefile
|
||||
|
||||
install: $(TESTS) $(HELPERS)
|
||||
@$(INSTALL) -d $(DESTDIR)$(libdir)/neon-tests
|
||||
@$(INSTALL) -d $(DESTDIR)$(libdir)/neon-tests/bin
|
||||
@$(INSTALL) -d $(DESTDIR)$(libdir)/neon-tests/data
|
||||
@for t in $(TESTS); do \
|
||||
$(LIBTOOL) --mode=install \
|
||||
$(INSTALL_PROGRAM) $$t $(DESTDIR)$(libdir)/neon-tests/bin; \
|
||||
done
|
||||
@for h in $(INSTALL_HELPERS); do \
|
||||
$(INSTALL_DATA) $$h $(DESTDIR)$(libdir)/neon-tests/data; \
|
||||
done
|
||||
@$(INSTALL_PROGRAM) $(srcdir)/run-tests.sh $(DESTDIR)$(libdir)/neon-tests
|
||||
|
||||
NEWS = $(top_srcdir)/NEWS
|
||||
|
||||
file1.gz: $(NEWS)
|
||||
gzip -c --no-name $(NEWS) > $@
|
||||
|
||||
file2.gz: $(NEWS)
|
||||
gzip -c --name $(NEWS) > $@
|
||||
|
||||
# gzip file with trailing bytes.
|
||||
trailing.gz: $(NEWS)
|
||||
gzip -c --no-name $(NEWS) > $@
|
||||
echo "hello, world" >> $@
|
||||
|
||||
truncated.gz: file1.gz
|
||||
dd if=file1.gz of=$@ bs=2048 count=2
|
||||
|
||||
badcsum.gz: file1.gz
|
||||
dd of=$@ if=file1.gz bs=1 count=`perl -e 'printf "%d", (stat("file1.gz"))[7] - 8;'`
|
||||
echo 'broken!' >> $@
|
||||
|
||||
corrupt1.gz: file1.gz
|
||||
dd of=$@ if=file1.gz bs=1 count=500
|
||||
cat $(NEWS) >> $@
|
||||
|
||||
corrupt2.gz:
|
||||
cat $(NEWS) > $@
|
||||
|
||||
empty.gz:
|
||||
touch $@
|
||||
|
||||
random.txt:
|
||||
cat $(NEWS) > $@
|
||||
|
||||
# Dummy target to create the CA keys etc. makekeys stderr is redirected
|
||||
# since it changes for every invocation; not helpful for regression
|
||||
# testing.
|
||||
ca-stamp: $(srcdir)/makekeys.sh $(srcdir)/openssl.conf
|
||||
rm -rf ca
|
||||
OPENSSL=$(OPENSSL) \
|
||||
$(SHELL) $(srcdir)/makekeys.sh $(srcdir) 2>makekeys.out
|
||||
@echo timestamp > ca-stamp
|
||||
|
||||
Makefile: $(srcdir)/Makefile.in
|
||||
cd .. && ./config.status test/Makefile
|
||||
|
||||
LIBOBJS = common/tests.lo common/child.lo utils.lo
|
||||
|
||||
$(LIBTEST): $(LIBOBJS)
|
||||
$(LINK) -o $(LIBTEST) $(LIBOBJS) $(LIBNEON)
|
||||
|
||||
.c.lo:
|
||||
$(COMPILE) -c $< -o $@
|
||||
|
||||
# Recompile socket.c with SOCKET_SSL defined
|
||||
socket-ssl.lo: $(srcdir)/socket.c $(HDRS)
|
||||
$(COMPILE) -DSOCKET_SSL -c $(srcdir)/socket.c -o $@
|
||||
|
||||
socket-ssl: socket-ssl.lo $(LIBTEST)
|
||||
$(LINK) -o $@ socket-ssl.lo $(LIBS)
|
||||
|
||||
resolve: resolve.lo $(LIBNEON)
|
||||
$(LINK) -o $@ resolve.lo $(LIBNEON)
|
||||
|
||||
common/tests.lo: $(srcdir)/common/tests.c $(OBJDEPS)
|
||||
common/child.lo: $(srcdir)/common/child.c $(OBJDEPS)
|
||||
utils.lo: $(srcdir)/utils.c $(OBJDEPS)
|
||||
auth.lo: $(srcdir)/auth.c $(OBJDEPS)
|
||||
uri-tests.lo: $(srcdir)/uri-tests.c $(OBJDEPS)
|
||||
util-tests.lo: $(srcdir)/util-tests.c $(OBJDEPS)
|
||||
string-tests.lo: $(srcdir)/string-tests.c $(OBJDEPS)
|
||||
socket.lo: $(srcdir)/socket.c $(OBJDEPS)
|
||||
server.lo: $(srcdir)/server.c $(OBJDEPS)
|
||||
request.lo: $(srcdir)/request.c $(OBJDEPS)
|
||||
regress.lo: $(srcdir)/regress.c $(OBJDEPS)
|
||||
compress.lo: $(srcdir)/compress.c $(OBJDEPS)
|
||||
acl.lo: $(srcdir)/acl.c $(OBJDEPS)
|
||||
utils.lo: $(srcdir)/utils.c $(OBJDEPS)
|
||||
stubs.lo: $(srcdir)/stubs.c $(OBJDEPS)
|
||||
props.lo: $(srcdir)/props.c $(OBJDEPS)
|
||||
session.lo: $(srcdir)/session.c $(OBJDEPS)
|
||||
redirect.lo: $(srcdir)/redirect.c $(OBJDEPS)
|
||||
basic.lo: $(srcdir)/basic.c $(OBJDEPS)
|
||||
ssl.lo: $(srcdir)/ssl.c $(OBJDEPS)
|
||||
lock.lo: $(srcdir)/lock.c $(OBJDEPS)
|
||||
largefile.lo: $(srcdir)/largefile.c $(OBJDEPS)
|
||||
|
||||
auth: auth.lo $(DEPS)
|
||||
$(LINK) -o $@ auth.lo $(DEPS)
|
||||
basic: basic.lo $(DEPS)
|
||||
$(LINK) -o $@ basic.lo $(DEPS)
|
||||
uri-tests: uri-tests.lo $(DEPS)
|
||||
$(LINK) -o $@ uri-tests.lo $(DEPS)
|
||||
util-tests: util-tests.lo $(DEPS)
|
||||
$(LINK) -o $@ util-tests.lo $(DEPS)
|
||||
string-tests: string-tests.lo $(DEPS)
|
||||
$(LINK) -o $@ string-tests.lo $(DEPS)
|
||||
socket: socket.lo $(DEPS)
|
||||
$(LINK) -o $@ socket.lo $(DEPS)
|
||||
server: server.lo $(DEPS)
|
||||
$(LINK) -o $@ server.lo $(DEPS)
|
||||
request: request.lo $(DEPS)
|
||||
$(LINK) -o $@ request.lo $(DEPS)
|
||||
regress: regress.lo $(DEPS)
|
||||
$(LINK) -o $@ regress.lo $(DEPS)
|
||||
compress: compress.lo $(DEPS)
|
||||
$(LINK) -o $@ compress.lo $(DEPS)
|
||||
acl: acl.lo $(DEPS)
|
||||
$(LINK) -o $@ acl.lo $(DEPS)
|
||||
utils: utils.lo $(DEPS)
|
||||
$(LINK) -o $@ utils.lo $(DEPS)
|
||||
stubs: stubs.lo $(DEPS)
|
||||
$(LINK) -o $@ stubs.lo $(DEPS)
|
||||
props: props.lo $(DEPS)
|
||||
$(LINK) -o $@ props.lo $(DEPS)
|
||||
session: session.lo $(DEPS)
|
||||
$(LINK) -o $@ session.lo $(DEPS)
|
||||
redirect: redirect.lo $(DEPS)
|
||||
$(LINK) -o $@ redirect.lo $(DEPS)
|
||||
ssl: ssl.lo $(DEPS)
|
||||
$(LINK) -o $@ ssl.lo $(DEPS)
|
||||
xml: xml.lo $(DEPS)
|
||||
$(LINK) -o $@ xml.lo $(DEPS)
|
||||
xmlreq: xmlreq.lo $(DEPS)
|
||||
$(LINK) -o $@ xmlreq.lo $(DEPS)
|
||||
lock: lock.lo $(DEPS)
|
||||
$(LINK) -o $@ lock.lo $(DEPS)
|
||||
largefile: largefile.lo $(DEPS)
|
||||
$(LINK) -o $@ largefile.lo $(DEPS)
|
||||
@@ -0,0 +1,39 @@
|
||||
|
||||
Stupidly Simple Test Suite for neon
|
||||
-----------------------------------
|
||||
|
||||
The aim of the test suite is two-fold:
|
||||
|
||||
1. ensure compliance to the relevant RFCs in network behaviour.
|
||||
|
||||
2. ensure that the promises made by the public API are met
|
||||
by the current implementation.
|
||||
|
||||
The file `STATUS' makes an attempt at listing RFC requirements and how
|
||||
the test suite tests whether neon meets them or not (it's not finished
|
||||
yet).
|
||||
|
||||
The test suite is licensed under the GPL.
|
||||
|
||||
Important Note About Test Failures
|
||||
----------------------------------
|
||||
|
||||
Note that a test failure either means a bug in the test or a bug in
|
||||
the code itself. On platforms without pipe(), there is a race
|
||||
condition in the code which forks a server process: if you get random
|
||||
failures on a slow or loaded box, increase the sleep time in
|
||||
common/child.c:minisleep().
|
||||
|
||||
Extra Stuff
|
||||
-----------
|
||||
|
||||
server-tests requires that you have a running HTTP server on localhost
|
||||
port 80, and you have copied htdocs/* to server-htdocs-root/test/*
|
||||
|
||||
Credits
|
||||
-------
|
||||
|
||||
This test suite is inspired by the Subversion project, discussion on
|
||||
the subversion mailing list, and seeing chromatic's talks on XP. The
|
||||
presentation is inspired by the standard Perl test suite. Imitation
|
||||
is the greatest form of flattery, right?
|
||||
@@ -0,0 +1,74 @@
|
||||
-*- text -*-
|
||||
|
||||
This document attempts to list RFC requirements and determine whether
|
||||
neon meets them, or where they do not apply, etc.
|
||||
|
||||
Yes: test written, succeeds
|
||||
No: test written, but currently fails
|
||||
???: no test written
|
||||
---: feature not supported
|
||||
App: this is an application issue not a neon issue
|
||||
|
||||
RFC2616
|
||||
=======
|
||||
|
||||
3.1: MUST treat major/minor as separate digits Yes
|
||||
3.1: MUST ignore leading zeros Yes
|
||||
3.1: MUST only send HTTP/1.1 when appropriate ???
|
||||
|
||||
3.2.2: MUST use abs_path of "/" in Request-URI App
|
||||
3.2.3: comparisons of host names MUST be case-insensitive Yes
|
||||
comparisons of scheme names MUST be ... Yes
|
||||
comparison of empty abs_path equivalent to "/" No/---
|
||||
|
||||
3.3.1: MUST accept three date formats App/Yes [2]
|
||||
MUST only generate RFC1123-style dates App
|
||||
|
||||
3.3.1: MUST use GMT for http-dates ???
|
||||
MUST assume GMT when parsing asctime dates ???
|
||||
|
||||
3.4.1: MUST respect charset label provided Yes/App
|
||||
|
||||
3.5*: content codings App
|
||||
|
||||
3.6: MUST requirements for multiple transfer-codings --- [4]
|
||||
|
||||
3.6.1: parsing of chunked transfer coding Yes
|
||||
MUST be able to handle "chunked" transfer-coding Yes
|
||||
MUST ignore unknown chunk-extension extensions Yes
|
||||
|
||||
3.7: parsing of Content-Type headers Yes
|
||||
|
||||
3.7: MUST NOT have LWS between type/subtype in C-T hdr App
|
||||
SHOULD only send parameters to "new HTTP apps" (>1.0?) App
|
||||
|
||||
3.7.1: MUST represent HTTP message in canonical form App
|
||||
MUST accept CRLF/CR/LF as line-breaks in text/* media App
|
||||
MUST NOT use only CR or LF in HTTP control structures ???
|
||||
MUST specify charset if not ISO-8859-1 App
|
||||
|
||||
3.7.2: multipart types ---
|
||||
|
||||
3.8: SHOULD have short product token Yes/App [5]
|
||||
SHOULD use product-version for version identifier Yes/App
|
||||
only product-version differs between versions Yes/App
|
||||
|
||||
3.9: Content Negotiation ---/App
|
||||
|
||||
3.10: Language Tags ---/App
|
||||
|
||||
3.11: Entity Tags ---/App
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[2]: date parser is provided which handles all three formats, but no
|
||||
handling of the Date header is present within neon.
|
||||
|
||||
[3]: not sure if neon should be handling of this internally.
|
||||
|
||||
[4]: neon only supports using just chunked Transfer-Coding or none.
|
||||
|
||||
[5]: these reflect that applications may add their own product tokens
|
||||
alongside neon's.
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
Dummy ACL tests
|
||||
Copyright (C) 2001-2003, Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "ne_acl.h"
|
||||
|
||||
#include "tests.h"
|
||||
#include "child.h"
|
||||
#include "utils.h"
|
||||
|
||||
/**** DUMMY TESTS: just makes sure the stuff doesn't dump core. */
|
||||
|
||||
static int test_acl(const char *uri, ne_acl_entry *es, int nume)
|
||||
{
|
||||
ne_session *sess;
|
||||
|
||||
CALL(make_session(&sess, single_serve_string,
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Connection: close\r\n\r\n"));
|
||||
|
||||
ON(ne_acl_set(sess, uri, es, nume));
|
||||
|
||||
CALL(await_server());
|
||||
ne_session_destroy(sess);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int grant_all(void)
|
||||
{
|
||||
ne_acl_entry e = {0};
|
||||
|
||||
e.apply = ne_acl_all;
|
||||
e.type = ne_acl_grant;
|
||||
|
||||
CALL(test_acl("/foo", &e, 1));
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int deny_all(void)
|
||||
{
|
||||
ne_acl_entry e = {0};
|
||||
|
||||
e.apply = ne_acl_all;
|
||||
e.type = ne_acl_deny;
|
||||
|
||||
CALL(test_acl("/foo", &e, 1));
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int deny_one(void)
|
||||
{
|
||||
ne_acl_entry e = {0};
|
||||
|
||||
e.apply = ne_acl_href;
|
||||
e.type = ne_acl_deny;
|
||||
e.principal = "http://webdav.org/users/joe";
|
||||
|
||||
CALL(test_acl("/foo", &e, 1));
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int deny_byprop(void)
|
||||
{
|
||||
ne_acl_entry e = {0};
|
||||
|
||||
e.apply = ne_acl_property;
|
||||
e.type = ne_acl_deny;
|
||||
e.principal = "owner";
|
||||
|
||||
CALL(test_acl("/foo", &e, 1));
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
ne_test tests[] = {
|
||||
T(grant_all),
|
||||
T(deny_all),
|
||||
T(deny_one),
|
||||
T(deny_byprop),
|
||||
T(NULL)
|
||||
};
|
||||
@@ -0,0 +1,383 @@
|
||||
/*
|
||||
Authentication tests
|
||||
Copyright (C) 2001-2005, Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "ne_request.h"
|
||||
#include "ne_auth.h"
|
||||
#include "ne_basic.h"
|
||||
|
||||
#include "tests.h"
|
||||
#include "child.h"
|
||||
#include "utils.h"
|
||||
|
||||
static const char username[] = "Aladdin", password[] = "open sesame";
|
||||
static int auth_failed;
|
||||
|
||||
#define BASIC_WALLY "Basic realm=WallyWorld"
|
||||
#define CHAL_WALLY "WWW-Authenticate: " BASIC_WALLY
|
||||
|
||||
#define EOL "\r\n"
|
||||
|
||||
static int auth_cb(void *userdata, const char *realm, int tries,
|
||||
char *un, char *pw)
|
||||
{
|
||||
if (strcmp(realm, "WallyWorld")) {
|
||||
NE_DEBUG(NE_DBG_HTTP, "Got wrong realm '%s'!\n", realm);
|
||||
return -1;
|
||||
}
|
||||
strcpy(un, username);
|
||||
strcpy(pw, password);
|
||||
return tries;
|
||||
}
|
||||
|
||||
static void auth_hdr(char *value)
|
||||
{
|
||||
#define B "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
|
||||
auth_failed = strcmp(value, B);
|
||||
NE_DEBUG(NE_DBG_HTTP, "Got auth header: [%s]\nWanted header: [%s]\n"
|
||||
"Result: %d\n", value, B, auth_failed);
|
||||
#undef B
|
||||
}
|
||||
|
||||
/* Sends a response with given response-code. If hdr is not NULL,
|
||||
* sends that header string too (appending an EOL). If eoc is
|
||||
* non-zero, request must be last sent down a connection; otherwise,
|
||||
* clength 0 is sent to maintain a persistent connection. */
|
||||
static int send_response(ne_socket *sock, const char *hdr, int code, int eoc)
|
||||
{
|
||||
char buffer[BUFSIZ];
|
||||
|
||||
sprintf(buffer, "HTTP/1.1 %d Blah Blah" EOL, code);
|
||||
|
||||
if (hdr) {
|
||||
strcat(buffer, hdr);
|
||||
strcat(buffer, EOL);
|
||||
}
|
||||
|
||||
if (eoc) {
|
||||
strcat(buffer, "Connection: close" EOL EOL);
|
||||
} else {
|
||||
strcat(buffer, "Content-Length: 0" EOL EOL);
|
||||
}
|
||||
|
||||
return SEND_STRING(sock, buffer);
|
||||
}
|
||||
|
||||
/* Server function which sends two responses: first requires auth,
|
||||
* second doesn't. */
|
||||
static int auth_serve(ne_socket *sock, void *userdata)
|
||||
{
|
||||
char *hdr = userdata;
|
||||
|
||||
auth_failed = 1;
|
||||
|
||||
/* Register globals for discard_request. */
|
||||
got_header = auth_hdr;
|
||||
want_header = "Authorization";
|
||||
|
||||
discard_request(sock);
|
||||
send_response(sock, hdr, 401, 0);
|
||||
|
||||
discard_request(sock);
|
||||
send_response(sock, NULL, auth_failed?500:200, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Test that various Basic auth challenges are correctly handled. */
|
||||
static int basic(void)
|
||||
{
|
||||
const char *hdrs[] = {
|
||||
/* simplest case */
|
||||
CHAL_WALLY,
|
||||
|
||||
/* several challenges, one header */
|
||||
"WWW-Authenticate: BarFooScheme, " BASIC_WALLY,
|
||||
|
||||
/* several challenges, one header */
|
||||
CHAL_WALLY ", BarFooScheme realm=\"PenguinWorld\"",
|
||||
|
||||
/* whitespace tests. */
|
||||
"WWW-Authenticate: Basic realm=WallyWorld ",
|
||||
|
||||
/* nego test. */
|
||||
"WWW-Authenticate: Negotiate fish, Basic realm=WallyWorld",
|
||||
|
||||
/* nego test. */
|
||||
"WWW-Authenticate: Negotiate fish, bar=boo, Basic realm=WallyWorld",
|
||||
|
||||
/* nego test. */
|
||||
"WWW-Authenticate: Negotiate, Basic realm=WallyWorld",
|
||||
|
||||
/* multi-header case 1 */
|
||||
"WWW-Authenticate: BarFooScheme\r\n"
|
||||
CHAL_WALLY,
|
||||
|
||||
/* multi-header cases 1 */
|
||||
CHAL_WALLY "\r\n"
|
||||
"WWW-Authenticate: BarFooScheme bar=\"foo\"",
|
||||
|
||||
/* multi-header case 3 */
|
||||
"WWW-Authenticate: FooBarChall foo=\"bar\"\r\n"
|
||||
CHAL_WALLY "\r\n"
|
||||
"WWW-Authenticate: BarFooScheme bar=\"foo\""
|
||||
};
|
||||
size_t n;
|
||||
|
||||
for (n = 0; n < sizeof(hdrs)/sizeof(hdrs[0]); n++) {
|
||||
ne_session *sess;
|
||||
|
||||
CALL(make_session(&sess, auth_serve, (void *)hdrs[n]));
|
||||
ne_set_server_auth(sess, auth_cb, NULL);
|
||||
|
||||
CALL(any_2xx_request(sess, "/norman"));
|
||||
|
||||
ne_session_destroy(sess);
|
||||
CALL(await_server());
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int retry_serve(ne_socket *sock, void *ud)
|
||||
{
|
||||
discard_request(sock);
|
||||
send_response(sock, CHAL_WALLY, 401, 0);
|
||||
|
||||
discard_request(sock);
|
||||
send_response(sock, CHAL_WALLY, 401, 0);
|
||||
|
||||
discard_request(sock);
|
||||
send_response(sock, NULL, 200, 0);
|
||||
|
||||
discard_request(sock);
|
||||
send_response(sock, CHAL_WALLY, 401, 0);
|
||||
|
||||
discard_request(sock);
|
||||
send_response(sock, NULL, 200, 0);
|
||||
|
||||
discard_request(sock);
|
||||
send_response(sock, NULL, 200, 0);
|
||||
|
||||
discard_request(sock);
|
||||
send_response(sock, NULL, 200, 0);
|
||||
|
||||
discard_request(sock);
|
||||
send_response(sock, CHAL_WALLY, 401, 0);
|
||||
|
||||
discard_request(sock);
|
||||
send_response(sock, NULL, 200, 0);
|
||||
|
||||
discard_request(sock);
|
||||
send_response(sock, CHAL_WALLY, 401, 0);
|
||||
|
||||
discard_request(sock);
|
||||
send_response(sock, CHAL_WALLY, 401, 0);
|
||||
|
||||
discard_request(sock);
|
||||
send_response(sock, CHAL_WALLY, 401, 0);
|
||||
|
||||
discard_request(sock);
|
||||
send_response(sock, NULL, 200, 0);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int retry_cb(void *userdata, const char *realm, int tries,
|
||||
char *un, char *pw)
|
||||
{
|
||||
int *count = userdata;
|
||||
|
||||
/* dummy creds; server ignores them anyway. */
|
||||
strcpy(un, "a");
|
||||
strcpy(pw, "b");
|
||||
|
||||
switch (*count) {
|
||||
case 0:
|
||||
case 1:
|
||||
if (tries == *count) {
|
||||
*count += 1;
|
||||
return 0;
|
||||
} else {
|
||||
t_context("On request #%d, got attempt #%d", *count, tries);
|
||||
*count = -1;
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
/* server fails a subsequent request, check that tries has
|
||||
* reset to zero. */
|
||||
if (tries == 0) {
|
||||
*count += 1;
|
||||
return 0;
|
||||
} else {
|
||||
t_context("On retry after failure #%d, tries was %d",
|
||||
*count, tries);
|
||||
*count = -1;
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
if (tries > 1) {
|
||||
t_context("Attempt counter reached #%d", tries);
|
||||
*count = -1;
|
||||
return 1;
|
||||
}
|
||||
return tries;
|
||||
default:
|
||||
t_context("Count reached %d!?", *count);
|
||||
*count = -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Test that auth retries are working correctly. */
|
||||
static int retries(void)
|
||||
{
|
||||
ne_session *sess;
|
||||
int count = 0;
|
||||
|
||||
CALL(make_session(&sess, retry_serve, NULL));
|
||||
|
||||
ne_set_server_auth(sess, retry_cb, &count);
|
||||
|
||||
/* This request will be 401'ed twice, then succeed. */
|
||||
ONREQ(any_request(sess, "/foo"));
|
||||
|
||||
/* auth_cb will have set up context. */
|
||||
CALL(count != 2);
|
||||
|
||||
/* this request will be 401'ed once, then succeed. */
|
||||
ONREQ(any_request(sess, "/foo"));
|
||||
|
||||
/* auth_cb will have set up context. */
|
||||
CALL(count != 3);
|
||||
|
||||
/* some 20x requests. */
|
||||
ONREQ(any_request(sess, "/foo"));
|
||||
ONREQ(any_request(sess, "/foo"));
|
||||
|
||||
/* this request will be 401'ed once, then succeed. */
|
||||
ONREQ(any_request(sess, "/foo"));
|
||||
|
||||
/* auth_cb will have set up context. */
|
||||
CALL(count != 4);
|
||||
|
||||
/* First request is 401'ed by the server at both attempts. */
|
||||
ONV(any_request(sess, "/foo") != NE_AUTH,
|
||||
("auth succeeded, should have failed: %s", ne_get_error(sess)));
|
||||
|
||||
count++;
|
||||
|
||||
/* Second request is 401'ed first time, then will succeed if
|
||||
* retried. 0.18.0 didn't reset the attempt counter though so
|
||||
* this didn't work. */
|
||||
ONV(any_request(sess, "/foo") == NE_AUTH,
|
||||
("auth failed on second try, should have succeeded: %s", ne_get_error(sess)));
|
||||
|
||||
ne_session_destroy(sess);
|
||||
|
||||
CALL(await_server());
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* crashes with neon <0.22 */
|
||||
static int forget_regress(void)
|
||||
{
|
||||
ne_session *sess = ne_session_create("http", "localhost", 7777);
|
||||
ne_forget_auth(sess);
|
||||
ne_session_destroy(sess);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int fail_auth_cb(void *ud, const char *realm, int attempt,
|
||||
char *un, char *pw)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* this may trigger a segfault in neon 0.21.x and earlier. */
|
||||
static int tunnel_regress(void)
|
||||
{
|
||||
ne_session *sess = ne_session_create("https", "localhost", 443);
|
||||
ne_session_proxy(sess, "localhost", 7777);
|
||||
ne_set_server_auth(sess, fail_auth_cb, NULL);
|
||||
CALL(spawn_server(7777, single_serve_string,
|
||||
"HTTP/1.1 401 Auth failed.\r\n"
|
||||
"WWW-Authenticate: Basic realm=asda\r\n"
|
||||
"Content-Length: 0\r\n\r\n"));
|
||||
any_request(sess, "/foo");
|
||||
ne_session_destroy(sess);
|
||||
CALL(await_server());
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* regression test for parsing a Negotiate challenge with on parameter
|
||||
* token. */
|
||||
static int negotiate_regress(void)
|
||||
{
|
||||
ne_session *sess = ne_session_create("http", "localhost", 7777);
|
||||
ne_set_server_auth(sess, fail_auth_cb, NULL);
|
||||
CALL(spawn_server(7777, single_serve_string,
|
||||
"HTTP/1.1 401 Auth failed.\r\n"
|
||||
"WWW-Authenticate: Negotiate\r\n"
|
||||
"Content-Length: 0\r\n\r\n"));
|
||||
any_request(sess, "/foo");
|
||||
ne_session_destroy(sess);
|
||||
CALL(await_server());
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
||||
/* test digest auth 2068-style. */
|
||||
|
||||
/* test digest auth 2617-style. */
|
||||
|
||||
/* test that digest has precedence over Basic for multi-scheme
|
||||
* challenges */
|
||||
|
||||
/* test auth-int, auth-int FAILURE. chunk trailers/non-trailer */
|
||||
|
||||
/* test logout */
|
||||
|
||||
/* proxy auth, proxy AND origin */
|
||||
|
||||
ne_test tests[] = {
|
||||
T(lookup_localhost),
|
||||
T(basic),
|
||||
T(retries),
|
||||
T(forget_regress),
|
||||
T(tunnel_regress),
|
||||
T(negotiate_regress),
|
||||
T(NULL)
|
||||
};
|
||||
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
Tests for high-level HTTP interface (ne_basic.h)
|
||||
Copyright (C) 2002-2005, Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "ne_basic.h"
|
||||
|
||||
#include "tests.h"
|
||||
#include "child.h"
|
||||
#include "utils.h"
|
||||
|
||||
static int content_type(void)
|
||||
{
|
||||
int n;
|
||||
static const struct {
|
||||
const char *value, *type, *subtype, *charset;
|
||||
} ctypes[] = {
|
||||
{ "foo/bar", "foo", "bar", NULL },
|
||||
{ "foo/bar ", "foo", "bar", NULL },
|
||||
{ "application/xml", "application", "xml", NULL },
|
||||
/* text/ subtypes default to charset ISO-8859-1, per 2616. */
|
||||
{ "text/lemon", "text", "lemon", "ISO-8859-1" },
|
||||
/* text/xml defaults to charset us-ascii, per 3280 */
|
||||
{ "text/xml", "text", "xml", "us-ascii" },
|
||||
#undef TXU
|
||||
#define TXU "text", "xml", "utf-8"
|
||||
/* 2616 doesn't *say* that charset can be quoted, but bets are
|
||||
* that some servers do it anyway. */
|
||||
{ "text/xml; charset=utf-8", TXU },
|
||||
{ "text/xml; charset=utf-8; foo=bar", TXU },
|
||||
{ "text/xml;charset=utf-8", TXU },
|
||||
{ "text/xml ;charset=utf-8", TXU },
|
||||
{ "text/xml;charset=utf-8;foo=bar", TXU },
|
||||
{ "text/xml; foo=bar; charset=utf-8", TXU },
|
||||
{ "text/xml; foo=bar; charset=utf-8; bar=foo", TXU },
|
||||
{ "text/xml; charset=\"utf-8\"", TXU },
|
||||
{ "text/xml; charset='utf-8'", TXU },
|
||||
{ "text/xml; foo=bar; charset=\"utf-8\"; bar=foo", TXU },
|
||||
#undef TXU
|
||||
/* badly quoted charset should come out as NULL */
|
||||
{ "foo/lemon; charset=\"utf-8", "foo", "lemon", NULL },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
for (n = 0; ctypes[n].value != NULL; n++) {
|
||||
ne_content_type ct;
|
||||
ne_session *sess;
|
||||
ne_request *req;
|
||||
char resp[200];
|
||||
int rv;
|
||||
|
||||
ct.type = ct.subtype = ct.charset = ct.value = "unset";
|
||||
|
||||
ne_snprintf(resp, sizeof resp,
|
||||
"HTTP/1.0 200 OK\r\n" "Content-Length: 0\r\n"
|
||||
"Content-Type: %s\r\n" "\r\n", ctypes[n].value);
|
||||
|
||||
CALL(make_session(&sess, single_serve_string, resp));
|
||||
|
||||
req = ne_request_create(sess, "GET", "/anyfoo");
|
||||
ONREQ(ne_request_dispatch(req));
|
||||
rv = ne_get_content_type(req, &ct);
|
||||
|
||||
ONV(rv == 0 && !ctypes[n].type,
|
||||
("expected c-t parse failure for %s", ctypes[n].value));
|
||||
|
||||
ONV(rv != 0 && ctypes[n].type,
|
||||
("c-t parse failure %d for %s", rv, ctypes[n].value));
|
||||
|
||||
ne_request_destroy(req);
|
||||
ne_session_destroy(sess);
|
||||
CALL(await_server());
|
||||
|
||||
if (rv) continue;
|
||||
|
||||
ONV(strcmp(ct.type, ctypes[n].type),
|
||||
("for `%s': type was `%s'", ctypes[n].value, ct.type));
|
||||
|
||||
ONV(strcmp(ct.subtype, ctypes[n].subtype),
|
||||
("for `%s': subtype was `%s'", ctypes[n].value, ct.subtype));
|
||||
|
||||
ONV(ctypes[n].charset && ct.charset == NULL,
|
||||
("for `%s': charset unset", ctypes[n].value));
|
||||
|
||||
ONV(ctypes[n].charset == NULL && ct.charset != NULL,
|
||||
("for `%s': unexpected charset `%s'", ctypes[n].value,
|
||||
ct.charset));
|
||||
|
||||
ONV(ctypes[n].charset && ct.charset &&
|
||||
strcmp(ctypes[n].charset, ct.charset),
|
||||
("for `%s': charset was `%s'", ctypes[n].value, ct.charset));
|
||||
|
||||
ne_free(ct.value);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* Do ranged GET for range 'start' to 'end'; with 'resp' as response.
|
||||
* If 'fail' is non-NULL, expect ne_get_range to fail, and fail the
|
||||
* test with given message if it doesn't. */
|
||||
static int do_range(off_t start, off_t end, const char *fail,
|
||||
char *resp)
|
||||
{
|
||||
ne_session *sess;
|
||||
ne_content_range range = {0};
|
||||
int fd, ret;
|
||||
|
||||
CALL(make_session(&sess, single_serve_string, resp));
|
||||
|
||||
range.start = start;
|
||||
range.end = end;
|
||||
|
||||
fd = open("/dev/null", O_WRONLY);
|
||||
|
||||
ret = ne_get_range(sess, "/foo", &range, fd);
|
||||
|
||||
close(fd);
|
||||
CALL(await_server());
|
||||
|
||||
if (fail) {
|
||||
#if 0
|
||||
t_warning("error was %s", ne_get_error(sess));
|
||||
#endif
|
||||
ONV(ret == NE_OK, ("%s", fail));
|
||||
} else {
|
||||
ONREQ(ret);
|
||||
}
|
||||
|
||||
ne_session_destroy(sess);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int get_range(void)
|
||||
{
|
||||
return do_range(1, 10, NULL,
|
||||
"HTTP/1.1 206 Widgets\r\n" "Connection: close\r\n"
|
||||
"Content-Range: bytes 1-10\r\n"
|
||||
"Content-Length: 10\r\n\r\nabcdefghij");
|
||||
}
|
||||
|
||||
static int fail_range_length(void)
|
||||
{
|
||||
return do_range(1, 10, "range response length mismatch should fail",
|
||||
"HTTP/1.1 206 Widgets\r\n" "Connection: close\r\n"
|
||||
"Content-Range: bytes 1-2\r\n"
|
||||
"Content-Length: 2\r\n\r\nab");
|
||||
}
|
||||
|
||||
static int fail_range_units(void)
|
||||
{
|
||||
return do_range(1, 2, "range response units check should fail",
|
||||
"HTTP/1.1 206 Widgets\r\n" "Connection: close\r\n"
|
||||
"Content-Range: fish 1-2\r\n"
|
||||
"Content-Length: 2\r\n\r\nab");
|
||||
}
|
||||
|
||||
static int fail_range_notrange(void)
|
||||
{
|
||||
return do_range(1, 2, "non-ranged response should fail",
|
||||
"HTTP/1.1 200 Widgets\r\n" "Connection: close\r\n"
|
||||
"Content-Range: bytes 1-2\r\n"
|
||||
"Content-Length: 2\r\n\r\nab");
|
||||
}
|
||||
|
||||
static int fail_range_unsatify(void)
|
||||
{
|
||||
return do_range(1, 2, "unsatisfiable range should fail",
|
||||
"HTTP/1.1 416 No Go\r\n" "Connection: close\r\n"
|
||||
"Content-Length: 2\r\n\r\nab");
|
||||
}
|
||||
|
||||
static int dav_capabilities(void)
|
||||
{
|
||||
static const struct {
|
||||
const char *hdrs;
|
||||
unsigned int class1, class2, exec;
|
||||
} caps[] = {
|
||||
{ "DAV: 1,2\r\n", 1, 1, 0 },
|
||||
{ "DAV: 1 2\r\n", 0, 0, 0 },
|
||||
/* these aren't strictly legal DAV: headers: */
|
||||
{ "DAV: 2,1\r\n", 1, 1, 0 },
|
||||
{ "DAV: 1, 2 \r\n", 1, 1, 0 },
|
||||
{ "DAV: 1\r\nDAV:2\r\n", 1, 1, 0 },
|
||||
{ NULL, 0, 0, 0 }
|
||||
};
|
||||
char resp[BUFSIZ];
|
||||
int n;
|
||||
|
||||
for (n = 0; caps[n].hdrs != NULL; n++) {
|
||||
ne_server_capabilities c = {0};
|
||||
ne_session *sess;
|
||||
|
||||
ne_snprintf(resp, BUFSIZ, "HTTP/1.0 200 OK\r\n"
|
||||
"Connection: close\r\n"
|
||||
"%s" "\r\n", caps[n].hdrs);
|
||||
|
||||
CALL(make_session(&sess, single_serve_string, resp));
|
||||
|
||||
ONREQ(ne_options(sess, "/foo", &c));
|
||||
|
||||
ONV(c.dav_class1 != caps[n].class1,
|
||||
("class1 was %d not %d", c.dav_class1, caps[n].class1));
|
||||
ONV(c.dav_class2 != caps[n].class2,
|
||||
("class2 was %d not %d", c.dav_class2, caps[n].class2));
|
||||
ONV(c.dav_executable != caps[n].exec,
|
||||
("class2 was %d not %d", c.dav_executable, caps[n].exec));
|
||||
|
||||
CALL(await_server());
|
||||
|
||||
ne_session_destroy(sess);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
ne_test tests[] = {
|
||||
T(lookup_localhost),
|
||||
T(content_type),
|
||||
T(get_range),
|
||||
T(fail_range_length),
|
||||
T(fail_range_units),
|
||||
T(fail_range_notrange),
|
||||
T(fail_range_unsatify),
|
||||
T(dav_capabilities),
|
||||
T(NULL)
|
||||
};
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
Wed Aug 25 19:27:26 2004 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* child.c (reset_socket): New function.
|
||||
|
||||
Mon Jul 5 18:38:08 2004 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* tests.c (main): Print 'xfail' for expected failures.
|
||||
|
||||
Tue Oct 7 21:19:56 2003 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* child.c (close_socket): New function.
|
||||
(server_child, spawn_server_repeat): Use it.
|
||||
|
||||
Fri Jul 25 12:13:59 2003 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
Add support for test type which is expected to fail
|
||||
memory leak checks.
|
||||
|
||||
* tests.h (T_XLEAKY, T_EXPECT_LEAKS): New defines.
|
||||
|
||||
* test.c (main) [NEON_MEMLEAK]: If T_EXPECT_LEAKS is set, fail if
|
||||
the test did not leak memory.
|
||||
|
||||
Wed Jun 18 20:10:45 2003 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* child.c (server_child, spawn_server_repeat): Adapt for new
|
||||
socket API.
|
||||
|
||||
Sun Mar 9 17:52:11 2003 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* test.h (T_EXPECT_FAIL): New constant.
|
||||
(T_XFAIL): New test function wrapper.
|
||||
|
||||
* tests.c (main): Handle expected failures.
|
||||
|
||||
Sat Mar 1 21:04:35 2003 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
Extend the ne_test structure with a 'flags' field which can
|
||||
optionally request leak checking at run-time.
|
||||
|
||||
* tests.h (ne_test): Add 'flags' field.
|
||||
(T_CHECK_LEAKS): New flag.
|
||||
(T): Use T_CHECK_LEAKS flag by default.
|
||||
(T_LEAKY): Like T, but with no flags set.
|
||||
|
||||
* tests.c (main) [NEON_MEMLEAK]: If leak checking is requested,
|
||||
if a test passes, but leaks memory, fail the test.
|
||||
|
||||
Wed Feb 26 21:52:15 2003 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* tests.c (main): Test the "disable debugging" mode of
|
||||
ne_debug_init, NE_DBG_FLUSH, and a ne_debug() with no output.
|
||||
|
||||
Fri Aug 23 22:54:35 2002 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* tests.c (main): Call ne_sock_init after ne_debug_init, so that
|
||||
debugging messages are caught from ne_sock_init. Print a warning
|
||||
message if ne_sock_init fails.
|
||||
|
||||
Wed Aug 21 13:29:20 2002 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* tests.h (ONCMP): New macro.
|
||||
|
||||
Mon Aug 19 16:53:20 2002 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* child.c (lookup_localhost): Just use inet_addr to resolve
|
||||
127.0.0.1.
|
||||
|
||||
Sun Aug 18 13:50:30 2002 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* tests.c (TEST_DEBUG): Add NE_DBG_SSL.
|
||||
|
||||
Sat Aug 10 10:19:18 2002 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* child.c (server_send): Fix declaration.
|
||||
(discard_body): Use NE_FMT_SSIZE_T for print ssize_t's.
|
||||
|
||||
Sat Jul 6 08:42:37 2002 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* child.c (discard_body): New function.
|
||||
|
||||
Sun Jun 30 10:26:33 2002 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* child.c (server_send): New function.
|
||||
(discard_request): Fail with appropriate error.
|
||||
|
||||
Sun Jun 30 09:03:51 2002 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* tests.c (main): Reap server after each test has run.
|
||||
|
||||
Sun Jun 30 09:00:43 2002 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* child.c (reap_server): Set `child' to 0 so child can't be reaped
|
||||
twice.
|
||||
|
||||
Sun Jun 23 12:09:09 2002 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* child.c (serve_file): Use large buffer when sending in chunked
|
||||
mode to support large chunk sizes.
|
||||
|
||||
Sun Jun 23 09:35:09 2002 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* child.c (serve_file): Use NE_FMT_OFF_T and NE_FMT_SSIZE_T.
|
||||
|
||||
Thu May 30 21:57:39 2002 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* child.c (minisleep): Export function.
|
||||
|
||||
Sun May 19 18:23:19 2002 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* child.c, tests.c: s/sock_/ne_sock_/, s/SOCK_/NE_SOCK_/ for
|
||||
socket layer API change.
|
||||
|
||||
* child.h (SEND_STRING): New macro.
|
||||
|
||||
Sun May 19 08:57:21 2002 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* child.c (lookup_hostname): Conditionally use hstrerror().
|
||||
|
||||
Mon Feb 25 20:54:56 2002 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* tests.c (t_context): Use ne_vsnprintf.
|
||||
|
||||
Mon Feb 11 21:52:23 2002 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* child.c (lookup_hostname): New function.
|
||||
(server_child, do_listen): Pass around struct in_addr argument.
|
||||
(spawn_server_addr): Renamed from spawn_server, take bind_local
|
||||
flag to use localhost or "real" hostname to bind to.
|
||||
(spawn_server): New function, use spawn_server
|
||||
|
||||
Mon Feb 11 20:51:27 2002 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* child.c (minisleep) [HAVE_USLEEP]: Use nice short usleep()
|
||||
rather than boring long sleep().
|
||||
|
||||
Sat Feb 2 14:15:25 2002 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* child.c (spawn_server_repeat): Exit child process (with failure)
|
||||
if the server callback fails.
|
||||
|
||||
Fri Jan 4 22:06:17 2002 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* tests.h: Add SKIPREST result value.
|
||||
|
||||
* tests.c (TEST_DEBUG): Add NE_DBG_LOCKS. (main): Support
|
||||
SKIPREST.
|
||||
|
||||
Tue Jan 1 20:36:58 2002 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* tests.h: Make test_suite symbol have external linkage.
|
||||
|
||||
Sat Nov 10 22:28:55 2001 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* tests.c, test.h: Export name of test suite.
|
||||
|
||||
Sun Nov 4 13:56:42 2001 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* child.c (discard_request): Support retrieving arbitrary header
|
||||
values from request via want_request, got_request globals.
|
||||
|
||||
Wed Oct 24 21:41:39 2001 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* tests.h (ONV): New macro. (ON, ONN): Redefine in terms of ONV.
|
||||
|
||||
Wed Oct 24 20:44:59 2001 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* tests.c (main, t_warning, segv): Use colours when stdout is a
|
||||
terminal device.
|
||||
|
||||
Wed Oct 24 20:36:38 2001 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* tests.c (t_context, t_warning): Renamed from i_am, warning.
|
||||
(t_context): Take printf varargs.
|
||||
|
||||
* tests.h (ONN, ON): Update, simplify.
|
||||
|
||||
* child.c: Update.
|
||||
|
||||
Tue Oct 23 22:15:17 2001 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* tests.c (main): Vertically align results after warnings.
|
||||
|
||||
Tue Oct 2 23:36:44 2001 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* child.c (do_listen): Refactored from server_socket, only does
|
||||
bind/listen. (spawn_server): Moved awaken/accept calls here.
|
||||
(spawn_server_repeat, dead_server): New functions.
|
||||
|
||||
Sun Sep 30 10:14:35 2001 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* tests.h: Use a function/name structure for tests, add 'T' macro
|
||||
for easily writing initializers. Rename 'name' global variable to
|
||||
'test_context' to avoid parameter name collisions.
|
||||
|
||||
* child.c (spawn_server): Update accordingly.
|
||||
|
||||
* tests.c (i_am): Update accordingly. (main): Update; prettify
|
||||
output using new 'name' from test structure. Cope better when all
|
||||
tests in a suite are skipped.
|
||||
|
||||
Sat Sep 29 11:04:40 2001 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* child.c (serve_file): If 'chunks' is set in argument object,
|
||||
then deliver the file as a series of chunks.
|
||||
|
||||
Thu Sep 27 20:28:45 2001 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* child.c (serve_file): New function.
|
||||
|
||||
Thu Sep 27 20:28:41 2001 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* child.c (discard_request): Reset clength.
|
||||
|
||||
Mon Aug 27 00:31:09 2001 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* tests.c (test_num): Expose test counter. (segv): Handle
|
||||
segfault nicely.
|
||||
|
||||
Mon Aug 27 00:30:20 2001 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* child.c (discard_request): New function, from request.c in
|
||||
neon/test.
|
||||
|
||||
Wed Aug 8 22:09:21 2001 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* tests.c, test.h: Only define test_argc/argv once.
|
||||
|
||||
Mon Jul 16 16:30:28 2001 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* tests.c (warning): Take printf-style arguments list.
|
||||
|
||||
Mon Jul 16 16:16:08 2001 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* tests.c (main): Cope with skipped tests properly.
|
||||
|
||||
Mon Jul 16 16:00:59 2001 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* tests.c (warning): New function. (main): Cope with warnings.
|
||||
|
||||
Sun Jul 8 16:09:33 2001 Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
* tests.c (main): Export argc/argv as test_argc, test_argv.
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
Simple test framework for neon; licensed under the GNU GPL.
|
||||
|
||||
Copyright (C) 2001-2002 Joe Orton
|
||||
@@ -0,0 +1,478 @@
|
||||
/*
|
||||
Framework for testing with a server process
|
||||
Copyright (C) 2001-2004, Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <sys/wait.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "ne_socket.h"
|
||||
#include "ne_utils.h"
|
||||
#include "ne_string.h"
|
||||
|
||||
#include "tests.h"
|
||||
#include "child.h"
|
||||
|
||||
static pid_t child = 0;
|
||||
|
||||
int clength;
|
||||
|
||||
static struct in_addr lh_addr = {0}, hn_addr = {0};
|
||||
|
||||
const char *want_header = NULL;
|
||||
got_header_fn got_header = NULL;
|
||||
char *local_hostname = NULL;
|
||||
|
||||
/* If we have pipe(), then use a pipe between the parent and child to
|
||||
* know when the child is ready to accept incoming connections.
|
||||
* Otherwise use boring sleep()s trying to avoid the race condition
|
||||
* between listen() and connect() in the two processes. */
|
||||
#ifdef HAVE_PIPE
|
||||
#define USE_PIPE 1
|
||||
#endif
|
||||
|
||||
int lookup_localhost(void)
|
||||
{
|
||||
/* this will break if a system is set up so that `localhost' does
|
||||
* not resolve to 127.0.0.1, but... */
|
||||
lh_addr.s_addr = inet_addr("127.0.0.1");
|
||||
return OK;
|
||||
}
|
||||
|
||||
int lookup_hostname(void)
|
||||
{
|
||||
char buf[BUFSIZ];
|
||||
struct hostent *ent;
|
||||
|
||||
local_hostname = NULL;
|
||||
ONV(gethostname(buf, BUFSIZ) < 0,
|
||||
("gethostname failed: %s", strerror(errno)));
|
||||
|
||||
ent = gethostbyname(buf);
|
||||
#ifdef HAVE_HSTRERROR
|
||||
ONV(ent == NULL,
|
||||
("could not resolve `%s': %s", buf, hstrerror(h_errno)));
|
||||
#else
|
||||
ONV(ent == NULL, ("could not resolve `%s'", buf));
|
||||
#endif
|
||||
|
||||
local_hostname = ne_strdup(ent->h_name);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int do_listen(struct in_addr addr, int port)
|
||||
{
|
||||
int ls = socket(AF_INET, SOCK_STREAM, 0);
|
||||
struct sockaddr_in saddr = {0};
|
||||
int val = 1;
|
||||
|
||||
setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, (void *)&val, sizeof(int));
|
||||
|
||||
saddr.sin_addr = addr;
|
||||
saddr.sin_port = htons(port);
|
||||
saddr.sin_family = AF_INET;
|
||||
|
||||
if (bind(ls, (struct sockaddr *)&saddr, sizeof(saddr))) {
|
||||
printf("bind failed: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (listen(ls, 5)) {
|
||||
printf("listen failed: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ls;
|
||||
}
|
||||
|
||||
void minisleep(void)
|
||||
{
|
||||
#ifdef HAVE_USLEEP
|
||||
usleep(500);
|
||||
#else
|
||||
sleep(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
int reset_socket(ne_socket *sock)
|
||||
{
|
||||
#ifdef SO_LINGER
|
||||
/* Stevens' magic trick to send an RST on close(). */
|
||||
struct linger l = {1, 0};
|
||||
return setsockopt(ne_sock_fd(sock), SOL_SOCKET, SO_LINGER, &l, sizeof l);
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* close 'sock', performing lingering close to avoid premature RST. */
|
||||
static int close_socket(ne_socket *sock)
|
||||
{
|
||||
#ifdef HAVE_SHUTDOWN
|
||||
char buf[20];
|
||||
int fd = ne_sock_fd(sock);
|
||||
|
||||
shutdown(fd, 0);
|
||||
while (ne_sock_read(sock, buf, sizeof buf) > 0);
|
||||
#endif
|
||||
return ne_sock_close(sock);
|
||||
}
|
||||
|
||||
/* This runs as the child process. */
|
||||
static int server_child(int readyfd, struct in_addr addr, int port,
|
||||
server_fn callback, void *userdata)
|
||||
{
|
||||
ne_socket *s = ne_sock_create();
|
||||
int ret, listener;
|
||||
|
||||
in_child();
|
||||
|
||||
listener = do_listen(addr, port);
|
||||
if (listener < 0)
|
||||
return FAIL;
|
||||
|
||||
#ifdef USE_PIPE
|
||||
/* Tell the parent we're ready for the request. */
|
||||
if (write(readyfd, "a", 1) != 1) abort();
|
||||
#endif
|
||||
|
||||
ONN("accept failed", ne_sock_accept(s, listener));
|
||||
|
||||
ret = callback(s, userdata);
|
||||
|
||||
close_socket(s);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int spawn_server(int port, server_fn fn, void *ud)
|
||||
{
|
||||
return spawn_server_addr(1, port, fn, ud);
|
||||
}
|
||||
|
||||
int spawn_server_addr(int bind_local, int port, server_fn fn, void *ud)
|
||||
{
|
||||
int fds[2];
|
||||
struct in_addr addr;
|
||||
|
||||
addr = bind_local?lh_addr:hn_addr;
|
||||
|
||||
#ifdef USE_PIPE
|
||||
if (pipe(fds)) {
|
||||
perror("spawn_server: pipe");
|
||||
return FAIL;
|
||||
}
|
||||
#else
|
||||
/* avoid using uninitialized variable. */
|
||||
fds[0] = fds[1] = 0;
|
||||
#endif
|
||||
|
||||
child = fork();
|
||||
|
||||
ONN("fork server", child == -1);
|
||||
|
||||
if (child == 0) {
|
||||
/* this is the child. */
|
||||
int ret;
|
||||
|
||||
ret = server_child(fds[1], addr, port, fn, ud);
|
||||
|
||||
#ifdef USE_PIPE
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
#endif
|
||||
|
||||
/* print the error out otherwise it gets lost. */
|
||||
if (ret) {
|
||||
printf("server child failed: %s\n", test_context);
|
||||
}
|
||||
|
||||
/* and quit the child. */
|
||||
exit(ret);
|
||||
} else {
|
||||
char ch;
|
||||
|
||||
#ifdef USE_PIPE
|
||||
if (read(fds[0], &ch, 1) < 0)
|
||||
perror("parent read");
|
||||
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
#else
|
||||
minisleep();
|
||||
#endif
|
||||
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
|
||||
int spawn_server_repeat(int port, server_fn fn, void *userdata, int n)
|
||||
{
|
||||
int fds[2];
|
||||
|
||||
#ifdef USE_PIPE
|
||||
if (pipe(fds)) {
|
||||
perror("spawn_server: pipe");
|
||||
return FAIL;
|
||||
}
|
||||
#else
|
||||
/* avoid using uninitialized variable. */
|
||||
fds[0] = fds[1] = 0;
|
||||
#endif
|
||||
|
||||
child = fork();
|
||||
|
||||
if (child == 0) {
|
||||
/* this is the child. */
|
||||
int listener, count = 0;
|
||||
|
||||
in_child();
|
||||
|
||||
listener = do_listen(lh_addr, port);
|
||||
|
||||
#ifdef USE_PIPE
|
||||
if (write(fds[1], "Z", 1) != 1) abort();
|
||||
#endif
|
||||
|
||||
close(fds[1]);
|
||||
close(fds[0]);
|
||||
|
||||
/* Loop serving requests. */
|
||||
while (++count < n) {
|
||||
ne_socket *sock = ne_sock_create();
|
||||
int ret;
|
||||
|
||||
NE_DEBUG(NE_DBG_HTTP, "child awaiting connection #%d.\n", count);
|
||||
ONN("accept failed", ne_sock_accept(sock, listener));
|
||||
ret = fn(sock, userdata);
|
||||
close_socket(sock);
|
||||
NE_DEBUG(NE_DBG_HTTP, "child served request, %d.\n", ret);
|
||||
if (ret) {
|
||||
printf("server child failed: %s\n", test_context);
|
||||
exit(-1);
|
||||
}
|
||||
/* don't send back notification to parent more than
|
||||
* once. */
|
||||
}
|
||||
|
||||
NE_DEBUG(NE_DBG_HTTP, "child aborted.\n");
|
||||
close(listener);
|
||||
|
||||
exit(-1);
|
||||
|
||||
} else {
|
||||
char ch;
|
||||
/* this is the parent. wait for the child to get ready */
|
||||
#ifdef USE_PIPE
|
||||
if (read(fds[0], &ch, 1) < 0)
|
||||
perror("parent read");
|
||||
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
#else
|
||||
minisleep();
|
||||
#endif
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int dead_server(void)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (waitpid(child, &status, WNOHANG)) {
|
||||
/* child quit already! */
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
NE_DEBUG(NE_DBG_HTTP, "child has not quit.\n");
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
||||
int await_server(void)
|
||||
{
|
||||
int status;
|
||||
|
||||
(void) wait(&status);
|
||||
|
||||
/* so that we aren't reaped by mistake. */
|
||||
child = 0;
|
||||
|
||||
ONN("error from server process", WEXITSTATUS(status));
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int reap_server(void)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (child != 0) {
|
||||
(void) kill(child, SIGTERM);
|
||||
minisleep();
|
||||
(void) wait(&status);
|
||||
child = 0;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
ssize_t server_send(ne_socket *sock, const char *str, size_t len)
|
||||
{
|
||||
NE_DEBUG(NE_DBG_HTTP, "Sending: %.*s\n", (int)len, str);
|
||||
return ne_sock_fullwrite(sock, str, len);
|
||||
}
|
||||
|
||||
int discard_request(ne_socket *sock)
|
||||
{
|
||||
char buffer[1024];
|
||||
size_t offset = want_header?strlen(want_header):0;
|
||||
|
||||
clength = 0;
|
||||
|
||||
NE_DEBUG(NE_DBG_HTTP, "Discarding request...\n");
|
||||
do {
|
||||
ONV(ne_sock_readline(sock, buffer, 1024) < 0,
|
||||
("error reading line: %s", ne_sock_error(sock)));
|
||||
NE_DEBUG(NE_DBG_HTTP, "[req] %s", buffer);
|
||||
if (strncasecmp(buffer, "content-length:", 15) == 0) {
|
||||
clength = atoi(buffer + 16);
|
||||
}
|
||||
if (got_header != NULL && want_header != NULL &&
|
||||
strncasecmp(buffer, want_header, offset) == 0 &&
|
||||
buffer[offset] == ':') {
|
||||
char *value = buffer + offset + 1;
|
||||
if (*value == ' ') value++;
|
||||
got_header(ne_shave(value, "\r\n"));
|
||||
}
|
||||
} while (strcmp(buffer, "\r\n") != 0);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int discard_body(ne_socket *sock)
|
||||
{
|
||||
while (clength > 0) {
|
||||
char buf[BUFSIZ];
|
||||
size_t bytes = clength;
|
||||
ssize_t ret;
|
||||
if (bytes > sizeof(buf)) bytes = sizeof(buf);
|
||||
NE_DEBUG(NE_DBG_HTTP, "Discarding %" NE_FMT_SIZE_T " bytes.\n",
|
||||
bytes);
|
||||
ret = ne_sock_read(sock, buf, bytes);
|
||||
ONV(ret < 0, ("socket read failed (%" NE_FMT_SSIZE_T "): %s",
|
||||
ret, ne_sock_error(sock)));
|
||||
clength -= ret;
|
||||
NE_DEBUG(NE_DBG_HTTP, "Got %" NE_FMT_SSIZE_T " bytes.\n", ret);
|
||||
}
|
||||
NE_DEBUG(NE_DBG_HTTP, "Discard successful.\n");
|
||||
return OK;
|
||||
}
|
||||
|
||||
int serve_file(ne_socket *sock, void *ud)
|
||||
{
|
||||
char buffer[BUFSIZ];
|
||||
struct stat st;
|
||||
struct serve_file_args *args = ud;
|
||||
ssize_t ret;
|
||||
int fd;
|
||||
|
||||
CALL(discard_request(sock));
|
||||
|
||||
ne_sock_fullread(sock, buffer, clength);
|
||||
|
||||
fd = open(args->fname, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
SEND_STRING(sock,
|
||||
"HTTP/1.0 404 File Not Found\r\n"
|
||||
"Content-Length: 0\r\n\r\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ONN("fstat fd", fstat(fd, &st));
|
||||
|
||||
SEND_STRING(sock, "HTTP/1.0 200 OK\r\n");
|
||||
if (args->chunks) {
|
||||
sprintf(buffer, "Transfer-Encoding: chunked\r\n");
|
||||
} else {
|
||||
sprintf(buffer, "Content-Length: %" NE_FMT_OFF_T "\r\n",
|
||||
st.st_size);
|
||||
}
|
||||
|
||||
if (args->headers) {
|
||||
strcat(buffer, args->headers);
|
||||
}
|
||||
|
||||
strcat(buffer, "\r\n");
|
||||
|
||||
SEND_STRING(sock, buffer);
|
||||
|
||||
NE_DEBUG(NE_DBG_HTTP, "Serving %s (%" NE_FMT_OFF_T " bytes).\n",
|
||||
args->fname, st.st_size);
|
||||
|
||||
if (args->chunks) {
|
||||
char buf[1024];
|
||||
|
||||
while ((ret = read(fd, &buf, args->chunks)) > 0) {
|
||||
/* this is a small integer, cast it explicitly to avoid
|
||||
* warnings with printing an ssize_t. */
|
||||
sprintf(buffer, "%x\r\n", (unsigned int)ret);
|
||||
SEND_STRING(sock, buffer);
|
||||
ONN("writing body", ne_sock_fullwrite(sock, buf, ret));
|
||||
SEND_STRING(sock, "\r\n");
|
||||
}
|
||||
|
||||
SEND_STRING(sock, "0\r\n\r\n");
|
||||
|
||||
} else {
|
||||
while ((ret = read(fd, buffer, BUFSIZ)) > 0) {
|
||||
ONN("writing body", ne_sock_fullwrite(sock, buffer, ret));
|
||||
}
|
||||
}
|
||||
|
||||
ONN("error reading from file", ret < 0);
|
||||
|
||||
(void) close(fd);
|
||||
|
||||
return OK;
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
Framework for testing with a server process
|
||||
Copyright (C) 2001-2004, Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef CHILD_H
|
||||
#define CHILD_H 1
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h> /* for strlen() */
|
||||
#endif
|
||||
|
||||
#include "ne_socket.h"
|
||||
|
||||
/* Test which does DNS lookup on "localhost": this must be the first
|
||||
* named test. */
|
||||
int lookup_localhost(void);
|
||||
|
||||
/* Test which looks up real local hostname. */
|
||||
int lookup_hostname(void);
|
||||
|
||||
/* set to local hostname if lookup_hostname succeeds. */
|
||||
extern char *local_hostname;
|
||||
|
||||
/* Callback for spawn_server. */
|
||||
typedef int (*server_fn)(ne_socket *sock, void *userdata);
|
||||
|
||||
/* Spawns server child process:
|
||||
* - forks child process.
|
||||
* - child process listens on localhost at given port.
|
||||
* - when you connect to it, 'fn' is run...
|
||||
* fn is passed the client/server socket as first argument,
|
||||
* and userdata as second.
|
||||
* - the socket is closed when 'fn' returns, so don't close in in 'fn'.
|
||||
*/
|
||||
int spawn_server(int port, server_fn fn, void *userdata);
|
||||
|
||||
/* Like spawn_server; if bind_local is non-zero, binds server to
|
||||
* localhost, otherwise, binds server to real local hostname. (must
|
||||
* have called lookup_localhost or lookup_hostname as approprate
|
||||
* beforehand). */
|
||||
int spawn_server_addr(int bind_local, int port, server_fn fn, void *userdata);
|
||||
|
||||
/* Like spawn server except will continue accepting connections and
|
||||
* processing requests, up to 'n' times. If 'n' is reached, then the
|
||||
* child process exits with a failure status. */
|
||||
int spawn_server_repeat(int port, server_fn fn, void *userdata, int n);
|
||||
|
||||
/* Blocks until child process exits, and gives return code of 'fn'. */
|
||||
int await_server(void);
|
||||
|
||||
/* Kills child process. */
|
||||
int reap_server(void);
|
||||
|
||||
/* Returns non-zero if server process has already died. */
|
||||
int dead_server(void);
|
||||
|
||||
/* If discard_request comes across a header called 'want_header', it
|
||||
* will call got_header passing the header field value. */
|
||||
extern const char *want_header;
|
||||
typedef void (*got_header_fn)(char *value);
|
||||
extern got_header_fn got_header;
|
||||
|
||||
/* Send string to child; ne_sock_fullwrite with debugging. */
|
||||
ssize_t server_send(ne_socket *sock, const char *data, size_t len);
|
||||
|
||||
/* Utility macro: send given string down socket. */
|
||||
#define SEND_STRING(sock, str) server_send((sock), (str), strlen((str)))
|
||||
|
||||
/* Tries to ensure that the socket will be closed using RST rather
|
||||
* than FIN. */
|
||||
int reset_socket(ne_socket *sock);
|
||||
|
||||
/* Utility function: discard request. Sets context on error. */
|
||||
int discard_request(ne_socket *sock);
|
||||
|
||||
/* Utility function: discard request body. Sets context on error. */
|
||||
int discard_body(ne_socket *sock);
|
||||
|
||||
struct serve_file_args {
|
||||
const char *fname;
|
||||
const char *headers;
|
||||
int chunks;
|
||||
};
|
||||
|
||||
/* Utility function: callback for spawn_server: pass pointer to
|
||||
* serve_file_args as userdata, and args->fname is served as a 200
|
||||
* request. If args->headers is non-NULL, it must be a set of
|
||||
* CRLF-terminated lines which is added in to the response headers.
|
||||
* If args->chunks is non-zero, the file is delivered using chunks of
|
||||
* that size. */
|
||||
int serve_file(ne_socket *sock, void *ud);
|
||||
|
||||
/* set to value of C-L header by discard_request. */
|
||||
extern int clength;
|
||||
|
||||
/* Sleep for a short time. */
|
||||
void minisleep(void);
|
||||
|
||||
#endif /* CHILD_H */
|
||||
@@ -0,0 +1,19 @@
|
||||
#!/bin/sh
|
||||
|
||||
rm -f debug.log
|
||||
rm -f child.log
|
||||
|
||||
# for shared builds.
|
||||
LD_LIBRARY_PATH=../src/.libs:$LD_LIBRARY_PATH
|
||||
export LD_LIBRARY_PATH
|
||||
|
||||
for f in $*; do
|
||||
if ./$f; then
|
||||
:
|
||||
else
|
||||
echo FAILURE
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,351 @@
|
||||
/*
|
||||
Stupidly simple test framework
|
||||
Copyright (C) 2001-2002, 2004, Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <sys/signal.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_SIGNAL_H
|
||||
#include <signal.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "ne_string.h"
|
||||
#include "ne_utils.h"
|
||||
#include "ne_socket.h"
|
||||
|
||||
#include "tests.h"
|
||||
#include "child.h"
|
||||
|
||||
char test_context[BUFSIZ];
|
||||
int have_context = 0;
|
||||
|
||||
static FILE *child_debug, *debug;
|
||||
|
||||
char **test_argv;
|
||||
int test_argc;
|
||||
|
||||
const char *test_suite;
|
||||
int test_num;
|
||||
|
||||
/* statistics for all tests so far */
|
||||
static int passes = 0, fails = 0, skipped = 0, warnings = 0;
|
||||
|
||||
/* per-test globals: */
|
||||
static int warned, aborted = 0;
|
||||
static const char *test_name; /* current test name */
|
||||
|
||||
static int use_colour = 0;
|
||||
|
||||
/* resource for ANSI escape codes:
|
||||
* http://www.isthe.com/chongo/tech/comp/ansi_escapes.html */
|
||||
#define COL(x) do { if (use_colour) printf("\033[" x "m"); } while (0)
|
||||
|
||||
#define NOCOL COL("00")
|
||||
|
||||
void t_context(const char *context, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, context);
|
||||
ne_vsnprintf(test_context, BUFSIZ, context, ap);
|
||||
va_end(ap);
|
||||
have_context = 1;
|
||||
}
|
||||
|
||||
void t_warning(const char *str, ...)
|
||||
{
|
||||
va_list ap;
|
||||
COL("43;01"); printf("WARNING:"); NOCOL;
|
||||
putchar(' ');
|
||||
va_start(ap, str);
|
||||
vprintf(str, ap);
|
||||
va_end(ap);
|
||||
warnings++;
|
||||
warned++;
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
#define TEST_DEBUG \
|
||||
(NE_DBG_HTTP | NE_DBG_SOCKET | NE_DBG_HTTPBODY | NE_DBG_HTTPAUTH | \
|
||||
NE_DBG_LOCKS | NE_DBG_XMLPARSE | NE_DBG_XML | NE_DBG_SSL)
|
||||
|
||||
#define W(m) do { if (write(0, m, strlen(m)) < 0) exit(99); } while(0)
|
||||
|
||||
#define W_RED(m) do { if (use_colour) W("\033[41;37;01m"); \
|
||||
W(m); if (use_colour) W("\033[00m\n"); } while (0);
|
||||
|
||||
/* Signal handler for child processes. */
|
||||
static void child_segv(int signo)
|
||||
{
|
||||
signal(SIGSEGV, SIG_DFL);
|
||||
signal(SIGABRT, SIG_DFL);
|
||||
W_RED("Fatal signal in child!");
|
||||
kill(getpid(), SIGSEGV);
|
||||
minisleep();
|
||||
}
|
||||
|
||||
/* Signal handler for parent process. */
|
||||
static void parent_segv(int signo)
|
||||
{
|
||||
signal(SIGSEGV, SIG_DFL);
|
||||
signal(SIGABRT, SIG_DFL);
|
||||
if (signo == SIGSEGV) {
|
||||
W_RED("FAILED - segmentation fault");
|
||||
} else if (signo == SIGABRT) {
|
||||
W_RED("ABORTED");
|
||||
}
|
||||
reap_server();
|
||||
kill(getpid(), SIGSEGV);
|
||||
minisleep();
|
||||
}
|
||||
|
||||
void in_child(void)
|
||||
{
|
||||
ne_debug_init(child_debug, TEST_DEBUG);
|
||||
NE_DEBUG(TEST_DEBUG, "**** Child forked for test %s ****\n", test_name);
|
||||
signal(SIGSEGV, child_segv);
|
||||
signal(SIGABRT, child_segv);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int n;
|
||||
static const char dots[] = "......................";
|
||||
|
||||
/* get basename(argv[0]) */
|
||||
test_suite = strrchr(argv[0], '/');
|
||||
if (test_suite == NULL) {
|
||||
test_suite = argv[0];
|
||||
} else {
|
||||
test_suite++;
|
||||
}
|
||||
|
||||
#if defined(HAVE_ISATTY) && defined(STDOUT_FILENO)
|
||||
if (isatty(STDOUT_FILENO)) {
|
||||
use_colour = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
test_argc = argc;
|
||||
test_argv = argv;
|
||||
|
||||
debug = fopen("debug.log", "a");
|
||||
if (debug == NULL) {
|
||||
fprintf(stderr, "%s: Could not open debug.log: %s\n", test_suite,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
child_debug = fopen("child.log", "a");
|
||||
if (child_debug == NULL) {
|
||||
fprintf(stderr, "%s: Could not open child.log: %s\n", test_suite,
|
||||
strerror(errno));
|
||||
fclose(debug);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tests[0].fn == NULL) {
|
||||
printf("-> no tests found in `%s'\n", test_suite);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* install special SEGV handler. */
|
||||
signal(SIGSEGV, parent_segv);
|
||||
signal(SIGABRT, parent_segv);
|
||||
|
||||
/* test the "no-debugging" mode of ne_debug. */
|
||||
ne_debug_init(NULL, 0);
|
||||
NE_DEBUG(TEST_DEBUG, "This message should go to /dev/null");
|
||||
|
||||
/* enable debugging for real. */
|
||||
ne_debug_init(debug, TEST_DEBUG);
|
||||
NE_DEBUG(TEST_DEBUG | NE_DBG_FLUSH, "Version string: %s\n",
|
||||
ne_version_string());
|
||||
|
||||
/* another silly test. */
|
||||
NE_DEBUG(0, "This message should also go to /dev/null");
|
||||
|
||||
if (ne_sock_init()) {
|
||||
COL("43;01"); printf("WARNING:"); NOCOL;
|
||||
printf(" Socket library initalization failed.\n");
|
||||
}
|
||||
|
||||
printf("-> running `%s':\n", test_suite);
|
||||
|
||||
for (n = 0; !aborted && tests[n].fn != NULL; n++) {
|
||||
int result, is_xfail = 0;
|
||||
#ifdef NEON_MEMLEAK
|
||||
size_t allocated = ne_alloc_used;
|
||||
int is_xleaky = 0;
|
||||
#endif
|
||||
|
||||
test_name = tests[n].name;
|
||||
printf("%2d. %s%.*s ", n, test_name,
|
||||
(int) (strlen(dots) - strlen(test_name)), dots);
|
||||
have_context = 0;
|
||||
test_num = n;
|
||||
warned = 0;
|
||||
fflush(stdout);
|
||||
NE_DEBUG(TEST_DEBUG, "******* Running test %d: %s ********\n",
|
||||
n, test_name);
|
||||
|
||||
/* run the test. */
|
||||
result = tests[n].fn();
|
||||
|
||||
#ifdef NEON_MEMLEAK
|
||||
/* issue warnings for memory leaks, if requested */
|
||||
if ((tests[n].flags & T_CHECK_LEAKS) && result == OK &&
|
||||
ne_alloc_used > allocated) {
|
||||
t_context("memory leak of %" NE_FMT_SIZE_T " bytes",
|
||||
ne_alloc_used - allocated);
|
||||
fprintf(debug, "Blocks leaked: ");
|
||||
ne_alloc_dump(debug);
|
||||
result = FAIL;
|
||||
} else if (tests[n].flags & T_EXPECT_LEAKS && result == OK &&
|
||||
ne_alloc_used == allocated) {
|
||||
t_context("expected memory leak not detected");
|
||||
result = FAIL;
|
||||
} else if (tests[n].flags & T_EXPECT_LEAKS && result == OK) {
|
||||
fprintf(debug, "Blocks leaked (expected): ");
|
||||
ne_alloc_dump(debug);
|
||||
is_xleaky = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (tests[n].flags & T_EXPECT_FAIL) {
|
||||
if (result == OK) {
|
||||
t_context("test passed but expected failure");
|
||||
result = FAIL;
|
||||
} else if (result == FAIL) {
|
||||
result = OK;
|
||||
is_xfail = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* align the result column if we've had warnings. */
|
||||
if (warned) {
|
||||
printf(" %s ", dots);
|
||||
}
|
||||
|
||||
switch (result) {
|
||||
case OK:
|
||||
if (is_xfail) {
|
||||
COL("32;07");
|
||||
printf("xfail");
|
||||
} else {
|
||||
COL("32");
|
||||
printf("pass");
|
||||
}
|
||||
NOCOL;
|
||||
if (warned) {
|
||||
printf(" (with %d warning%s)", warned, (warned > 1)?"s":"");
|
||||
}
|
||||
#ifdef NEON_MEMLEAK
|
||||
if (is_xleaky) {
|
||||
printf(" (with expected leak, %" NE_FMT_SIZE_T " bytes)",
|
||||
ne_alloc_used - allocated);
|
||||
}
|
||||
#endif
|
||||
putchar('\n');
|
||||
passes++;
|
||||
break;
|
||||
case FAILHARD:
|
||||
aborted = 1;
|
||||
/* fall-through */
|
||||
case FAIL:
|
||||
COL("41;37;01"); printf("FAIL"); NOCOL;
|
||||
if (have_context) {
|
||||
printf(" (%s)", test_context);
|
||||
}
|
||||
putchar('\n');
|
||||
fails++;
|
||||
break;
|
||||
case SKIPREST:
|
||||
aborted = 1;
|
||||
/* fall-through */
|
||||
case SKIP:
|
||||
COL("44;37;01"); printf("SKIPPED"); NOCOL;
|
||||
if (have_context) {
|
||||
printf(" (%s)", test_context);
|
||||
}
|
||||
putchar('\n');
|
||||
skipped++;
|
||||
break;
|
||||
default:
|
||||
COL("41;37;01"); printf("OOPS"); NOCOL;
|
||||
printf(" unexpected test result `%d'\n", result);
|
||||
break;
|
||||
}
|
||||
|
||||
reap_server();
|
||||
}
|
||||
|
||||
/* discount skipped tests */
|
||||
if (skipped) {
|
||||
printf("-> %d %s.\n", skipped,
|
||||
skipped==1?"test was skipped":"tests were skipped");
|
||||
n -= skipped;
|
||||
if (passes + fails != n) {
|
||||
printf("-> ARGH! Number of test results does not match "
|
||||
"number of tests.\n"
|
||||
"-> ARGH! Test Results are INRELIABLE.\n");
|
||||
}
|
||||
}
|
||||
/* print the summary. */
|
||||
if (skipped && n == 0) {
|
||||
printf("<- all tests skipped for `%s'.\n", test_suite);
|
||||
} else {
|
||||
printf("<- summary for `%s': "
|
||||
"of %d tests run: %d passed, %d failed. %.1f%%\n",
|
||||
test_suite, n, passes, fails, 100*(float)passes/n);
|
||||
if (warnings) {
|
||||
printf("-> %d warning%s issued.\n", warnings,
|
||||
warnings==1?" was":"s were");
|
||||
}
|
||||
}
|
||||
|
||||
if (fclose(debug)) {
|
||||
fprintf(stderr, "Error closing debug.log: %s\n", strerror(errno));
|
||||
fails = 1;
|
||||
}
|
||||
|
||||
if (fclose(child_debug)) {
|
||||
fprintf(stderr, "Error closing child.log: %s\n", strerror(errno));
|
||||
fails = 1;
|
||||
}
|
||||
|
||||
ne_sock_exit();
|
||||
|
||||
return fails;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
Stupidly simple test framework
|
||||
Copyright (C) 2001-2005, Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
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., 59 Temple Place - Suite 330, Boston,
|
||||
MA 02111-1307, USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef TESTS_H
|
||||
#define TESTS_H 1
|
||||
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define OK 0
|
||||
#define FAIL 1
|
||||
#define FAILHARD 2 /* fail and skip all succeeding tests in this suite. */
|
||||
#define SKIP 3 /* test was skipped because precondition was not met */
|
||||
#define SKIPREST 4 /* skipped, and skip all succeeding tests in suite */
|
||||
|
||||
/* A test function. Must return any of OK, FAIL, FAILHARD, SKIP, or
|
||||
* SKIPREST. May call t_warning() any number of times. If not
|
||||
* returning OK, optionally call t_context to provide an error
|
||||
* message. */
|
||||
typedef int (*test_func)(void);
|
||||
|
||||
typedef struct {
|
||||
test_func fn; /* the function to test. */
|
||||
const char *name; /* the name of the test. */
|
||||
int flags;
|
||||
} ne_test;
|
||||
|
||||
/* possible values for flags: */
|
||||
#define T_CHECK_LEAKS (1) /* check for memory leaks */
|
||||
#define T_EXPECT_FAIL (2) /* expect failure */
|
||||
#define T_EXPECT_LEAKS (4) /* expect memory leak failures */
|
||||
|
||||
/* array of tests to run: must be defined by each test suite. */
|
||||
extern ne_test tests[];
|
||||
|
||||
/* define a test function which has the same name as the function,
|
||||
* and does check for memory leaks. */
|
||||
#define T(fn) { fn, #fn, T_CHECK_LEAKS }
|
||||
/* define a test function which is expected to return FAIL. */
|
||||
#define T_XFAIL(fn) { fn, #fn, T_EXPECT_FAIL | T_CHECK_LEAKS }
|
||||
/* define a test function which isn't checked for memory leaks. */
|
||||
#define T_LEAKY(fn) { fn, #fn, 0 }
|
||||
/* define a test function which is expected to fail memory leak checks */
|
||||
#define T_XLEAKY(fn) { fn, #fn, T_EXPECT_LEAKS }
|
||||
|
||||
/* current test number */
|
||||
extern int test_num;
|
||||
|
||||
/* name of test suite */
|
||||
extern const char *test_suite;
|
||||
|
||||
/* Provide result context message. */
|
||||
void t_context(const char *ctx, ...)
|
||||
#ifdef __GNUC__
|
||||
__attribute__ ((format (printf, 1, 2)))
|
||||
#endif /* __GNUC__ */
|
||||
;
|
||||
|
||||
extern char test_context[];
|
||||
|
||||
/* the command-line arguments passed in to the test suite: */
|
||||
extern char **test_argv;
|
||||
extern int test_argc;
|
||||
|
||||
/* child process should call this. */
|
||||
void in_child(void);
|
||||
|
||||
/* issue a warning. */
|
||||
void t_warning(const char *str, ...)
|
||||
#ifdef __GNUC__
|
||||
__attribute__ ((format (printf, 1, 2)))
|
||||
#endif /* __GNUC__ */
|
||||
;
|
||||
|
||||
/* Macros for easily writing is-not-zero comparison tests; the ON*
|
||||
* macros fail the function if a comparison is not zero.
|
||||
*
|
||||
* ONV(x,vs) takes a comparison X, and a printf varargs list for
|
||||
* the failure message.
|
||||
* e.g. ONV(strcmp(bar, "foo"), ("bar was %s not 'foo'", bar))
|
||||
*
|
||||
* ON(x) takes a comparison X, and uses the line number for the failure
|
||||
* message. e.g. ONV(strcmp(bar, "foo"))
|
||||
*
|
||||
* ONN(n, x) takes a comparison X, and a flat string failure message.
|
||||
* e.g. ONN("foo was wrong", strcmp(bar, "foo")) */
|
||||
|
||||
#define ONV(x,vs) do { if ((x)) { t_context vs; return FAIL; } } while (0)
|
||||
#define ON(x) ONV((x), ("line %d", __LINE__ ))
|
||||
#define ONN(n,x) ONV((x), (n))
|
||||
|
||||
/* ONCMP(exp, act, ctx, name): 'exp' is the expected string, 'act' is the
|
||||
* actual string for some field 'name'. Succeeds if strcmp(exp,act)
|
||||
* == 0 or both are NULL. */
|
||||
#define ONCMP(exp, act, ctx, name) do { \
|
||||
ONV(exp && !act, ("%s: " name " was NULL, expected '%s'", ctx, exp)); \
|
||||
ONV(!exp && act, ("%s: " name " was '%s', expected NULL", ctx, act)); \
|
||||
ONV(exp && strcmp(exp, act), ("%s: " name " was '%s' not '%s'", ctx, act, exp)); \
|
||||
} while (0)
|
||||
|
||||
/* return immediately with result of test 'x' if it fails. */
|
||||
#define CALL(x) do { int t_ret = (x); if (t_ret != OK) return t_ret; } while (0)
|
||||
|
||||
/* PRECOND: skip current test if condition 'x' is not true. */
|
||||
#define PRECOND(x) do { if (!(x)) { return SKIP; } } while (0)
|
||||
|
||||
#endif /* TESTS_H */
|
||||
@@ -0,0 +1,447 @@
|
||||
/*
|
||||
tests for compressed response handling.
|
||||
Copyright (C) 2001-2005, Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "ne_compress.h"
|
||||
#include "ne_auth.h"
|
||||
|
||||
#include "tests.h"
|
||||
#include "child.h"
|
||||
#include "utils.h"
|
||||
|
||||
static enum { f_partial = 0, f_mismatch, f_complete } failed;
|
||||
|
||||
static const char *newsfn = "random.txt";
|
||||
|
||||
static int init(void)
|
||||
{
|
||||
return lookup_localhost();
|
||||
}
|
||||
|
||||
#define EXTRA_DEBUG 0 /* disabled by default */
|
||||
|
||||
static int reader(void *ud, const char *block, size_t len)
|
||||
{
|
||||
struct string *b = ud;
|
||||
|
||||
#if EXTRA_DEBUG
|
||||
NE_DEBUG(NE_DBG_HTTP, "reader: got (%d): [[[%.*s]]]\n", (int)len,
|
||||
(int)len, block);
|
||||
#endif
|
||||
|
||||
if (failed == f_mismatch) return -1;
|
||||
|
||||
/* catch multiple len == 0 call as issued by 0.25.0 only: */
|
||||
if (failed == f_complete) {
|
||||
NE_DEBUG(NE_DBG_HTTP, "reader: called after complete, len=%d\n",
|
||||
(int)len);
|
||||
failed = f_mismatch;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (failed == f_partial && len == 0) {
|
||||
if (b->len != 0) {
|
||||
NE_DEBUG(NE_DBG_HTTP, "reader: got length %d at EOF\n",
|
||||
(int)b->len);
|
||||
failed = f_mismatch;
|
||||
} else {
|
||||
failed = f_complete;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (len > b->len || memcmp(b->data, block, len) != 0) {
|
||||
NE_DEBUG(NE_DBG_HTTP, "reader: failed, got [[%.*s]] not [[%.*s]]\n",
|
||||
(int)len, block, (int)b->len, b->data);
|
||||
failed = f_mismatch;
|
||||
return -1;
|
||||
} else {
|
||||
b->data += len;
|
||||
b->len -= len;
|
||||
#if EXTRA_DEBUG
|
||||
NE_DEBUG(NE_DBG_HTTP, "reader: OK, %d bytes remaining\n",
|
||||
(int)b->len);
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int file2buf(int fd, ne_buffer *buf)
|
||||
{
|
||||
char buffer[BUFSIZ];
|
||||
ssize_t n;
|
||||
|
||||
while ((n = read(fd, buffer, BUFSIZ)) > 0) {
|
||||
ne_buffer_append(buf, buffer, n);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_fetch(const char *realfn, const char *gzipfn,
|
||||
int chunked, int expect_fail)
|
||||
{
|
||||
ne_session *sess;
|
||||
ne_request *req;
|
||||
int fd, ret;
|
||||
ne_buffer *buf = ne_buffer_create();
|
||||
struct serve_file_args sfargs;
|
||||
ne_decompress *dc;
|
||||
struct string body;
|
||||
|
||||
fd = open(realfn, O_RDONLY);
|
||||
ONN("failed to open file", fd < 0);
|
||||
file2buf(fd, buf);
|
||||
(void) close(fd);
|
||||
|
||||
body.data = buf->data;
|
||||
body.len = buf->used - 1;
|
||||
|
||||
failed = f_partial;
|
||||
|
||||
if (gzipfn) {
|
||||
sfargs.fname = gzipfn;
|
||||
sfargs.headers = "Content-Encoding: gzip\r\n";
|
||||
} else {
|
||||
sfargs.fname = realfn;
|
||||
sfargs.headers = NULL;
|
||||
}
|
||||
sfargs.chunks = chunked;
|
||||
|
||||
CALL(make_session(&sess, serve_file, &sfargs));
|
||||
|
||||
req = ne_request_create(sess, "GET", "/");
|
||||
dc = ne_decompress_reader(req, ne_accept_2xx, reader, &body);
|
||||
|
||||
#ifdef NE_DEBUGGING
|
||||
ne_debug_init(ne_debug_stream, ne_debug_mask & ~NE_DBG_HTTPBODY);
|
||||
#endif
|
||||
|
||||
ret = ne_request_dispatch(req);
|
||||
|
||||
#ifdef NE_DEBUGGING
|
||||
ne_debug_init(ne_debug_stream, ne_debug_mask | NE_DBG_HTTPBODY);
|
||||
#endif
|
||||
|
||||
ONN("file not served", ne_get_status(req)->code != 200);
|
||||
|
||||
ONN("decompress succeeded", expect_fail && !ret);
|
||||
ONV(!expect_fail && ret, ("decompress failed: %s", ne_get_error(sess)));
|
||||
|
||||
NE_DEBUG(NE_DBG_HTTP, "session error: %s\n", ne_get_error(sess));
|
||||
|
||||
ne_decompress_destroy(dc);
|
||||
ne_request_destroy(req);
|
||||
ne_session_destroy(sess);
|
||||
ne_buffer_destroy(buf);
|
||||
|
||||
if (expect_fail) {
|
||||
/* if the decompress callback fails, the connection may
|
||||
* be aborted and hence the server will abort. */
|
||||
reap_server();
|
||||
} else {
|
||||
CALL(await_server());
|
||||
}
|
||||
|
||||
if (!expect_fail) {
|
||||
ONN("inflated response truncated", failed == f_partial);
|
||||
ONN("inflated response mismatch", failed == f_mismatch);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int fetch(const char *realfn, const char *gzipfn, int chunked)
|
||||
{
|
||||
return do_fetch(realfn, gzipfn, chunked, 0);
|
||||
}
|
||||
|
||||
/* Test the no-compression case. */
|
||||
static int not_compressed(void)
|
||||
{
|
||||
return fetch(newsfn, NULL, 0);
|
||||
}
|
||||
|
||||
static int simple(void)
|
||||
{
|
||||
return fetch(newsfn, "file1.gz", 0);
|
||||
}
|
||||
|
||||
/* file1.gz has an embedded filename. */
|
||||
static int withname(void)
|
||||
{
|
||||
return fetch(newsfn, "file2.gz", 0);
|
||||
}
|
||||
|
||||
/* deliver various different sizes of chunks: tests the various
|
||||
* decoding cases. */
|
||||
static int chunked_1b_wn(void)
|
||||
{
|
||||
return fetch(newsfn, "file2.gz", 1);
|
||||
}
|
||||
|
||||
static int chunked_1b(void)
|
||||
{
|
||||
return fetch(newsfn, "file1.gz", 1);
|
||||
}
|
||||
|
||||
static int chunked_12b(void)
|
||||
{
|
||||
return fetch(newsfn, "file2.gz", 12);
|
||||
}
|
||||
|
||||
static int chunked_20b(void)
|
||||
{
|
||||
return fetch(newsfn, "file2.gz", 20);
|
||||
}
|
||||
|
||||
static int chunked_10b(void)
|
||||
{
|
||||
return fetch(newsfn, "file1.gz", 10);
|
||||
}
|
||||
|
||||
static int chunked_10b_wn(void)
|
||||
{
|
||||
return fetch(newsfn, "file2.gz", 10);
|
||||
}
|
||||
|
||||
static int fail_trailing(void)
|
||||
{
|
||||
return do_fetch(newsfn, "trailing.gz", 0, 1);
|
||||
}
|
||||
|
||||
static int fail_trailing_1b(void)
|
||||
{
|
||||
return do_fetch(newsfn, "trailing.gz", 1, 1);
|
||||
}
|
||||
|
||||
static int fail_truncate(void)
|
||||
{
|
||||
return do_fetch(newsfn, "truncated.gz", 0, 1);
|
||||
}
|
||||
|
||||
static int fail_bad_csum(void)
|
||||
{
|
||||
return do_fetch(newsfn, "badcsum.gz", 0, 1);
|
||||
}
|
||||
|
||||
static int fail_corrupt1(void)
|
||||
{
|
||||
return do_fetch(newsfn, "corrupt1.gz", 0, 1);
|
||||
}
|
||||
|
||||
static int fail_corrupt2(void)
|
||||
{
|
||||
return do_fetch(newsfn, "corrupt2.gz", 0, 1);
|
||||
}
|
||||
|
||||
static int fail_empty(void)
|
||||
{
|
||||
return do_fetch(newsfn, "empty.gz", 0, 1);
|
||||
}
|
||||
|
||||
static int notcomp_empty(void)
|
||||
{
|
||||
return fetch("empty.gz", NULL, 0);
|
||||
}
|
||||
|
||||
static int auth_cb(void *userdata, const char *realm, int tries,
|
||||
char *un, char *pw)
|
||||
{
|
||||
strcpy(un, "foo");
|
||||
strcpy(pw, "bar");
|
||||
return tries;
|
||||
}
|
||||
|
||||
static int retry_compress_helper(ne_accept_response acceptor,
|
||||
struct string *resp, struct string *expect)
|
||||
{
|
||||
ne_session *sess;
|
||||
ne_request *req;
|
||||
ne_decompress *dc;
|
||||
|
||||
CALL(make_session(&sess, serve_sstring, resp));
|
||||
|
||||
ne_set_server_auth(sess, auth_cb, NULL);
|
||||
|
||||
req = ne_request_create(sess, "GET", "/");
|
||||
dc = ne_decompress_reader(req, acceptor, reader, expect);
|
||||
failed = f_partial;
|
||||
|
||||
ONREQ(ne_request_dispatch(req));
|
||||
|
||||
ne_decompress_destroy(dc);
|
||||
|
||||
ONN("got bad response body", failed != f_complete);
|
||||
|
||||
CALL(await_server());
|
||||
|
||||
ne_request_destroy(req);
|
||||
ne_session_destroy(sess);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static char retry_gz_resp[] =
|
||||
"HTTP/1.1 401 Get Away\r\n"
|
||||
"Content-Encoding: gzip\r\n"
|
||||
"WWW-Authenticate: Basic realm=WallyWorld\r\n"
|
||||
"Content-Length: 5\r\n"
|
||||
"\r\n"
|
||||
"abcde"
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Server: foo\r\n"
|
||||
"Content-Length: 5\r\n"
|
||||
"Connection: close\r\n"
|
||||
"\r\n"
|
||||
"hello";
|
||||
|
||||
/* Test where the response to the retried request does *not* have
|
||||
* a content-encoding, whereas the original 401 response did. */
|
||||
static int retry_notcompress(void)
|
||||
{
|
||||
struct string response = { retry_gz_resp, strlen(retry_gz_resp) };
|
||||
struct string expect = { "hello", 5 };
|
||||
return retry_compress_helper(ne_accept_2xx, &response, &expect);
|
||||
}
|
||||
|
||||
static char retry_gz_resp2[] =
|
||||
"HTTP/1.1 401 Get Away\r\n"
|
||||
"Content-Encoding: gzip\r\n"
|
||||
"WWW-Authenticate: Basic realm=WallyWorld\r\n"
|
||||
"Content-Length: 25\r\n"
|
||||
"\r\n"
|
||||
"\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03\xcb\x48\xcd\xc9\xc9\x07"
|
||||
"\x00\x86\xa6\x10\x36\x05\x00\x00\x00"
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Server: foo\r\n"
|
||||
"Content-Encoding: gzip\r\n"
|
||||
"Content-Length: 25\r\n"
|
||||
"Connection: close\r\n"
|
||||
"\r\n"
|
||||
"\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03\x2b\xcf\x2f\xca\x49\x01"
|
||||
"\x00\x43\x11\x77\x3a\x05\x00\x00\x00";
|
||||
|
||||
static int retry_accept(void *ud, ne_request *req, const ne_status *st)
|
||||
{
|
||||
struct string *expect = ud;
|
||||
|
||||
NE_DEBUG(NE_DBG_HTTP, "retry_accept callback for %d response\n",
|
||||
st->code);
|
||||
|
||||
if (expect->len == 4 && strcmp(expect->data, "fish") == 0) {
|
||||
/* first time through */
|
||||
expect->data = "hello";
|
||||
} else {
|
||||
expect->data = "world";
|
||||
}
|
||||
|
||||
expect->len = 5;
|
||||
failed = f_partial; /* reset the state */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Test where the response to the retried request *does* have a
|
||||
* content-encoding, as did the original 401 response. */
|
||||
static int retry_compress(void)
|
||||
{
|
||||
struct string resp = { retry_gz_resp2, sizeof(retry_gz_resp2) - 1 };
|
||||
struct string expect = { "fish", 4 };
|
||||
return retry_compress_helper(retry_accept, &resp, &expect);
|
||||
}
|
||||
|
||||
#define READER_ABORT_ERR "reader_abort error string"
|
||||
|
||||
static int reader_abort(void *ud, const char *buf, size_t len)
|
||||
{
|
||||
ne_session *sess = ud;
|
||||
ne_set_error(sess, READER_ABORT_ERR);
|
||||
return len;
|
||||
}
|
||||
|
||||
/* check that a callback abort does abort the response */
|
||||
static int compress_abort(void)
|
||||
{
|
||||
ne_session *sess;
|
||||
ne_request *req;
|
||||
struct serve_file_args sfargs;
|
||||
ne_decompress *dc;
|
||||
int ret;
|
||||
|
||||
sfargs.fname = "file1.gz";
|
||||
sfargs.headers = "Content-Encoding: gzip\r\n";
|
||||
sfargs.chunks = 0;
|
||||
|
||||
CALL(make_session(&sess, serve_file, &sfargs));
|
||||
|
||||
req = ne_request_create(sess, "GET", "/abort");
|
||||
|
||||
dc = ne_decompress_reader(req, ne_accept_2xx, reader_abort, sess);
|
||||
|
||||
ret = ne_request_dispatch(req);
|
||||
|
||||
reap_server();
|
||||
|
||||
ONN("request was not aborted", ret != NE_ERROR);
|
||||
ONV(strcmp(ne_get_error(sess), READER_ABORT_ERR),
|
||||
("session error was %s not %s",
|
||||
ne_get_error(sess), READER_ABORT_ERR));
|
||||
|
||||
reap_server();
|
||||
ne_decompress_destroy(dc);
|
||||
ne_request_destroy(req);
|
||||
ne_session_destroy(sess);
|
||||
|
||||
return OK;
|
||||
|
||||
}
|
||||
|
||||
ne_test tests[] = {
|
||||
T_LEAKY(init),
|
||||
T(not_compressed),
|
||||
T(simple),
|
||||
T(withname),
|
||||
T(fail_trailing),
|
||||
T(fail_trailing_1b),
|
||||
T(fail_bad_csum),
|
||||
T(fail_truncate),
|
||||
T(fail_corrupt1),
|
||||
T(fail_corrupt2),
|
||||
T(fail_empty),
|
||||
T(notcomp_empty),
|
||||
T(chunked_1b),
|
||||
T(chunked_1b_wn),
|
||||
T(chunked_12b),
|
||||
T(chunked_20b),
|
||||
T(chunked_10b),
|
||||
T(chunked_10b_wn),
|
||||
T(retry_notcompress),
|
||||
T(retry_compress),
|
||||
T(compress_abort),
|
||||
T(NULL)
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDODCCAuKgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBoTELMAkGA1UEBhMCR0Ix
|
||||
FzAVBgNVBAgTDkNhbWJyaWRnZXNoaXJlMRIwEAYDVQQHEwlDYW1icmlkZ2UxGjAY
|
||||
BgNVBAoTEU5lb24gSGFja2VycyBMdGQuMRUwEwYDVQQLEwxOZW9uIFFBIERlcHQx
|
||||
EjAQBgNVBAMTCWxvY2FsaG9zdDEeMBwGCSqGSIb3DQEJARYPbmVvbkB3ZWJkYXYu
|
||||
b3JnMB4XDTAyMDEyMTIwMzkwNFoXDTAyMDEzMTIwMzkwNFowgaExCzAJBgNVBAYT
|
||||
AkdCMRcwFQYDVQQIEw5DYW1icmlkZ2VzaGlyZTESMBAGA1UEBxMJQ2FtYnJpZGdl
|
||||
MRowGAYDVQQKExFOZW9uIEhhY2tlcnMgTHRkLjEVMBMGA1UECxMMTmVvbiBRQSBE
|
||||
ZXB0MRIwEAYDVQQDEwlsb2NhbGhvc3QxHjAcBgkqhkiG9w0BCQEWD25lb25Ad2Vi
|
||||
ZGF2Lm9yZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDzRU5sZ8+CWQPvPkqJw9Kl
|
||||
oEgT2FqzZR9RT/qbJuRBmRphiRr0g7JOh5Mr7LXaKShedFLhGidutyKKwIZJnRht
|
||||
AgMBAAGjggEBMIH+MB0GA1UdDgQWBBRFA3ktzHSuD9uB6mJOWoElmOtknzCBzgYD
|
||||
VR0jBIHGMIHDgBRFA3ktzHSuD9uB6mJOWoElmOtkn6GBp6SBpDCBoTELMAkGA1UE
|
||||
BhMCR0IxFzAVBgNVBAgTDkNhbWJyaWRnZXNoaXJlMRIwEAYDVQQHEwlDYW1icmlk
|
||||
Z2UxGjAYBgNVBAoTEU5lb24gSGFja2VycyBMdGQuMRUwEwYDVQQLEwxOZW9uIFFB
|
||||
IERlcHQxEjAQBgNVBAMTCWxvY2FsaG9zdDEeMBwGCSqGSIb3DQEJARYPbmVvbkB3
|
||||
ZWJkYXYub3JnggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADQQBDSFbe
|
||||
9EjP+IyZ4vhJSk66gLSN8CnafoGm5JHpNOHy5gWLh7j0a/dxWRd4gpoBYBB6Y9rO
|
||||
YV6Eq3njdj0gu+NN
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1 @@
|
||||
Test file.
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
Tests for LFS support in neon
|
||||
Copyright (C) 2004, Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifdef HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include "ne_request.h"
|
||||
|
||||
#include "child.h"
|
||||
#include "utils.h"
|
||||
#include "tests.h"
|
||||
|
||||
#ifndef INT64_C
|
||||
#define INT64_C(x) x ## LL
|
||||
#endif
|
||||
|
||||
static const char data[] = "Hello, world.\n";
|
||||
static off64_t point = INT64_C(2) << 32;
|
||||
#define SPARSE "sparse.bin"
|
||||
|
||||
/* make a sparse large file */
|
||||
static int make_sparse_file(void)
|
||||
{
|
||||
int fd = open64(SPARSE, O_CREAT | O_TRUNC | O_WRONLY, 0644);
|
||||
|
||||
ONN("could not create large file " SPARSE, fd < 0);
|
||||
ONN("seek to point", lseek64(fd, point, SEEK_SET) != point);
|
||||
ONN("could not write to file",
|
||||
write(fd, data, strlen(data)) != (ssize_t)strlen(data));
|
||||
ONN("close failed", close(fd));
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* server function which checks that the request body sent was the
|
||||
* same as the 'data' array. */
|
||||
static int serve_check_body(ne_socket *sock, void *userdata)
|
||||
{
|
||||
CALL(discard_request(sock));
|
||||
|
||||
if (clength != (ssize_t)strlen(data)) {
|
||||
CALL(discard_body(sock));
|
||||
SEND_STRING(sock, "HTTP/1.0 400 Bad Request Body Length\r\n"
|
||||
"\r\n");
|
||||
} else {
|
||||
char buf[20];
|
||||
|
||||
if (ne_sock_fullread(sock, buf, clength) == 0) {
|
||||
SEND_STRING(sock, "HTTP/1.0 200 OK Then!\r\n\r\n");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sends a small segment of the file from a high offset. */
|
||||
static int send_high_offset(void)
|
||||
{
|
||||
int ret, fd = open64(SPARSE, O_RDONLY);
|
||||
ne_session *sess;
|
||||
ne_request *req;
|
||||
|
||||
ONN("could not open sparse file", fd < 0);
|
||||
|
||||
CALL(make_session(&sess, serve_check_body, NULL));
|
||||
|
||||
req = ne_request_create(sess, "PUT", "/sparse");
|
||||
ne_set_request_body_fd64(req, fd, point, strlen(data));
|
||||
ret = ne_request_dispatch(req);
|
||||
CALL(await_server());
|
||||
ONV(ret != NE_OK || ne_get_status(req)->klass != 2,
|
||||
("request failed: %s", ne_get_error(sess)));
|
||||
ne_request_destroy(req);
|
||||
ne_session_destroy(sess);
|
||||
close(fd);
|
||||
return OK;
|
||||
}
|
||||
|
||||
#if 1
|
||||
#define RESPSIZE INT64_C(4295008256)
|
||||
#define RESPSTR "4295008256"
|
||||
#else
|
||||
#define RESPSIZE INT64_C(2147491840) /* 2^31+8192 */
|
||||
#define RESPSTR "2147491840"
|
||||
#endif
|
||||
|
||||
/* Reads a request, sends a large response, reads a request, then
|
||||
* sends a little response. */
|
||||
static int serve_large_response(ne_socket *sock, void *ud)
|
||||
{
|
||||
int n = 0;
|
||||
char empty[8192];
|
||||
|
||||
CALL(discard_request(sock));
|
||||
|
||||
SEND_STRING(sock,
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Content-Length: " RESPSTR "\r\n"
|
||||
"Server: BigFileServerTM\r\n" "\r\n");
|
||||
|
||||
memset(empty, 0, sizeof empty);
|
||||
|
||||
for (n = 0; n < RESPSIZE/sizeof(empty); n++) {
|
||||
if (ne_sock_fullwrite(sock, empty, sizeof empty)) {
|
||||
NE_DEBUG(NE_DBG_SOCKET, "fullwrite failed\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
NE_DEBUG(NE_DBG_SOCKET, "Wrote %d lots of %d\n", n, (int)sizeof empty);
|
||||
|
||||
CALL(discard_request(sock));
|
||||
|
||||
SEND_STRING(sock, "HTTP/1.1 200 OK\r\n"
|
||||
"Connection: close\r\n\r\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_large_response(void)
|
||||
{
|
||||
ne_session *sess;
|
||||
ne_request *req;
|
||||
off64_t count = 0;
|
||||
int ret;
|
||||
char buf[8192];
|
||||
|
||||
CALL(make_session(&sess, serve_large_response, NULL));
|
||||
|
||||
req = ne_request_create(sess, "GET", "/foo");
|
||||
|
||||
#ifdef NE_DEBUGGING
|
||||
ne_debug_init(ne_debug_stream, ne_debug_mask & ~(NE_DBG_HTTPBODY|NE_DBG_HTTP));
|
||||
#endif
|
||||
|
||||
ret = ne_begin_request(req);
|
||||
if (ret == NE_OK) {
|
||||
while ((ret = ne_read_response_block(req, buf, sizeof buf)) > 0)
|
||||
count += ret;
|
||||
if (ret == NE_OK)
|
||||
ret = ne_end_request(req);
|
||||
}
|
||||
|
||||
#ifdef NE_DEBUGGING
|
||||
ne_debug_init(ne_debug_stream, ne_debug_mask & (NE_DBG_HTTPBODY|NE_DBG_HTTP));
|
||||
#endif
|
||||
|
||||
ONV(ret, ("request failed: %s", ne_get_error(sess)));
|
||||
ONV(count != RESPSIZE,
|
||||
("response body was %" NE_FMT_OFF64_T " not %" NE_FMT_OFF64_T,
|
||||
count, RESPSIZE));
|
||||
|
||||
ne_request_destroy(req);
|
||||
|
||||
CALL(any_2xx_request(sess, "/bar"));
|
||||
CALL(await_server());
|
||||
ne_session_destroy(sess);
|
||||
return OK;
|
||||
}
|
||||
|
||||
ne_test tests[] = {
|
||||
T(make_sparse_file),
|
||||
T(send_high_offset),
|
||||
T(read_large_response),
|
||||
T(NULL),
|
||||
};
|
||||
@@ -0,0 +1,590 @@
|
||||
/*
|
||||
lock tests
|
||||
Copyright (C) 2002-2005, Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "ne_request.h"
|
||||
#include "ne_locks.h"
|
||||
#include "ne_socket.h"
|
||||
#include "ne_basic.h"
|
||||
#include "ne_auth.h"
|
||||
|
||||
#include "tests.h"
|
||||
#include "child.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define EOL "\r\n"
|
||||
|
||||
/* returns an activelock XML element. */
|
||||
static char *activelock(enum ne_lock_scope scope,
|
||||
int depth,
|
||||
const char *owner,
|
||||
long timeout,
|
||||
const char *token_href)
|
||||
{
|
||||
static char buf[BUFSIZ];
|
||||
|
||||
ne_snprintf(buf, BUFSIZ,
|
||||
"<D:activelock>\n"
|
||||
"<D:locktype><D:write/></D:locktype>\n"
|
||||
"<D:lockscope><D:%s/></D:lockscope>\n"
|
||||
"<D:depth>%d</D:depth>\n"
|
||||
"<D:owner>%s</D:owner>\n"
|
||||
"<D:timeout>Second-%ld</D:timeout>\n"
|
||||
"<D:locktoken><D:href>%s</D:href></D:locktoken>\n"
|
||||
"</D:activelock>",
|
||||
scope==ne_lockscope_exclusive?"exclusive":"shared",
|
||||
depth, owner, timeout, token_href);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* return body of LOCK response for given lock. */
|
||||
static char *lock_response(enum ne_lock_scope scope,
|
||||
int depth,
|
||||
const char *owner,
|
||||
long timeout,
|
||||
const char *token_href)
|
||||
{
|
||||
static char buf[BUFSIZ];
|
||||
sprintf(buf,
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
|
||||
"<D:prop xmlns:D=\"DAV:\">"
|
||||
"<D:lockdiscovery>%s</D:lockdiscovery></D:prop>\n",
|
||||
activelock(scope, depth, owner, timeout, token_href));
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* return body of LOCK response where response gives multiple
|
||||
* activelocks (i.e. shared locks). */
|
||||
static char *multi_lock_response(struct ne_lock **locks)
|
||||
{
|
||||
ne_buffer *buf = ne_buffer_create();
|
||||
int n;
|
||||
|
||||
ne_buffer_zappend(buf,
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
|
||||
"<D:prop xmlns:D=\"DAV:\">"
|
||||
"<D:lockdiscovery>");
|
||||
|
||||
for (n = 0; locks[n] != NULL; n++) {
|
||||
char *lk = activelock(locks[n]->scope, locks[n]->depth,
|
||||
locks[n]->owner, locks[n]->timeout,
|
||||
locks[n]->token);
|
||||
ne_buffer_zappend(buf, lk);
|
||||
}
|
||||
|
||||
ne_buffer_zappend(buf, "</D:lockdiscovery></D:prop>");
|
||||
return ne_buffer_finish(buf);
|
||||
}
|
||||
|
||||
static char *discover_response(const char *href, const struct ne_lock *lk)
|
||||
{
|
||||
static char buf[BUFSIZ];
|
||||
ne_snprintf(buf, BUFSIZ,
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
|
||||
"<D:multistatus xmlns:D='DAV:'>\n"
|
||||
"<D:response><D:href>%s</D:href><D:propstat>\n"
|
||||
"<D:prop><D:lockdiscovery>%s</D:lockdiscovery></D:prop>\n"
|
||||
"<D:status>HTTP/1.1 200 OK</D:status></D:propstat>\n"
|
||||
"</D:response></D:multistatus>\n",
|
||||
href, activelock(lk->scope, lk->depth, lk->owner,
|
||||
7200, lk->token));
|
||||
return buf;
|
||||
}
|
||||
|
||||
static struct ne_lock *make_lock(const char *path, const char *token,
|
||||
enum ne_lock_scope scope, int depth)
|
||||
{
|
||||
struct ne_lock *lock = ne_calloc(sizeof *lock);
|
||||
|
||||
if (lock->token) lock->token = ne_strdup(token);
|
||||
lock->scope = scope;
|
||||
lock->depth = depth;
|
||||
lock->uri.host = ne_strdup("localhost");
|
||||
lock->uri.scheme = ne_strdup("http");
|
||||
lock->uri.path = ne_strdup(path);
|
||||
lock->uri.port = 7777;
|
||||
|
||||
return lock;
|
||||
}
|
||||
|
||||
/* Tests for lock store handling. */
|
||||
static int store_single(void)
|
||||
{
|
||||
ne_lock_store *store = ne_lockstore_create();
|
||||
struct ne_lock *lk = make_lock("/foo", "blah", ne_lockscope_exclusive, 0);
|
||||
struct ne_lock *lk2;
|
||||
|
||||
ONN("create failed", store == NULL);
|
||||
|
||||
ONN("new lock store not empty", ne_lockstore_first(store) != NULL);
|
||||
|
||||
ne_lockstore_add(store, lk);
|
||||
|
||||
ONN("lock not found in store", ne_lockstore_first(store) != lk);
|
||||
|
||||
ONN(">1 locks in store?", ne_lockstore_next(store) != NULL);
|
||||
|
||||
lk2 = ne_lockstore_findbyuri(store, &lk->uri);
|
||||
|
||||
ONN("lock not found by URI", lk2 == NULL);
|
||||
ONN("other lock found by URI", lk2 != lk);
|
||||
|
||||
ne_lockstore_remove(store, lk);
|
||||
|
||||
ONN("store not empty after removing lock",
|
||||
ne_lockstore_first(store) != NULL);
|
||||
|
||||
ONN("lock still found after removing lock",
|
||||
ne_lockstore_findbyuri(store, &lk->uri) != NULL);
|
||||
|
||||
ne_lockstore_destroy(store);
|
||||
ne_lock_destroy(lk);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int store_several(void)
|
||||
{
|
||||
ne_lock_store *store = ne_lockstore_create();
|
||||
struct ne_lock *lk = make_lock("/foo", "blah", ne_lockscope_exclusive, 0);
|
||||
struct ne_lock *lk2 = make_lock("/bar", "blee", ne_lockscope_exclusive, 0);
|
||||
struct ne_lock *lf, *lf2;
|
||||
|
||||
ONN("create failed", store == NULL);
|
||||
|
||||
ne_lockstore_add(store, lk);
|
||||
ne_lockstore_add(store, lk2);
|
||||
|
||||
lf = ne_lockstore_first(store);
|
||||
ONN("lock store empty", lf == NULL);
|
||||
lf2 = ne_lockstore_next(store);
|
||||
ONN("lock store >2 locks", ne_lockstore_next(store) != NULL);
|
||||
|
||||
/* guarantee that _first, _next returned either of the
|
||||
* combinations: (lf, lf2) or (lf2, lf) */
|
||||
ONN("found wrong locks", ((lf != lk && lf != lk2) ||
|
||||
(lf2 != lk && lf2 != lk2) ||
|
||||
(lf == lf2)));
|
||||
|
||||
ONN("first find failed",
|
||||
ne_lockstore_findbyuri(store, &lk->uri) != lk);
|
||||
ONN("second find failed",
|
||||
ne_lockstore_findbyuri(store, &lk2->uri) != lk2);
|
||||
|
||||
ne_lockstore_remove(store, lk);
|
||||
ne_lock_destroy(lk);
|
||||
|
||||
ONN("remove left stray lock?", ne_lockstore_first(store) != lk2);
|
||||
|
||||
ONN("remove left >1 lock?", ne_lockstore_next(store) != NULL);
|
||||
|
||||
ne_lockstore_remove(store, lk2);
|
||||
ne_lock_destroy(lk2);
|
||||
|
||||
ONN("store not empty after removing all locks",
|
||||
ne_lockstore_first(store) != NULL);
|
||||
|
||||
ne_lockstore_destroy(store);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* regression test for <= 0.18.2, where timeout field was not parsed correctly. */
|
||||
static int lock_timeout(void)
|
||||
{
|
||||
ne_session *sess;
|
||||
char *resp, *rbody = lock_response(ne_lockscope_exclusive, 0, "me",
|
||||
6500, "opaquelocktoken:foo");
|
||||
struct ne_lock *lock = ne_lock_create();
|
||||
|
||||
resp = ne_concat("HTTP/1.1 200 OK\r\n" "Server: neon-test-server\r\n"
|
||||
"Lock-Token: <opaquelocktoken:foo>" EOL
|
||||
"Connection: close\r\n\r\n", rbody, NULL);
|
||||
|
||||
CALL(make_session(&sess, single_serve_string, resp));
|
||||
ne_free(resp);
|
||||
|
||||
ne_fill_server_uri(sess, &lock->uri);
|
||||
lock->uri.path = ne_strdup("/foo");
|
||||
lock->timeout = 5;
|
||||
|
||||
ONREQ(ne_lock(sess, lock));
|
||||
|
||||
ONN("lock timeout ignored in response",
|
||||
lock->timeout != 6500);
|
||||
|
||||
ne_session_destroy(sess);
|
||||
ne_lock_destroy(lock);
|
||||
|
||||
CALL(await_server());
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int verify_if;
|
||||
static const char *verify_if_expect;
|
||||
|
||||
static void got_if_header(char *value)
|
||||
{
|
||||
verify_if = !strcmp(verify_if_expect, value);
|
||||
NE_DEBUG(NE_DBG_HTTP, "Verified If header, %d: got [%s] expected [%s]\n",
|
||||
verify_if, value, verify_if_expect);
|
||||
}
|
||||
|
||||
/* Server callback which checks that an If: header is recevied. */
|
||||
static int serve_verify_if(ne_socket *sock, void *userdata)
|
||||
{
|
||||
/* tell us about If headers in the request. */
|
||||
want_header = "If";
|
||||
got_header = got_if_header;
|
||||
verify_if_expect = userdata;
|
||||
|
||||
verify_if = 0;
|
||||
|
||||
CALL(discard_request(sock));
|
||||
|
||||
if (verify_if) {
|
||||
ON(SEND_STRING(sock, "HTTP/1.1 200 OK" EOL));
|
||||
} else {
|
||||
ON(SEND_STRING(sock, "HTTP/1.1 403 Wrong If Header" EOL));
|
||||
}
|
||||
|
||||
ON(SEND_STRING(sock, "Connection: close" EOL EOL));
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* Make a request which will require a lock. */
|
||||
static int do_request(ne_session *sess, const char *path, int depth,
|
||||
int modparent)
|
||||
{
|
||||
ne_request *req = ne_request_create(sess, "RANDOM", path);
|
||||
|
||||
if (depth > 0) {
|
||||
ne_add_depth_header(req, depth);
|
||||
}
|
||||
|
||||
if (depth != -1)
|
||||
ne_lock_using_resource(req, path, depth);
|
||||
if (modparent)
|
||||
ne_lock_using_parent(req, path);
|
||||
|
||||
ONREQ(ne_request_dispatch(req));
|
||||
|
||||
ONV(ne_get_status(req)->code != 200,
|
||||
("request failed: %s", ne_get_error(sess)));
|
||||
|
||||
ne_request_destroy(req);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* Tests If: header submission, for a lock of depth 'lockdepth' at
|
||||
* 'lockpath', with a request to 'reqpath' which Depth header of
|
||||
* 'reqdepth'. If modparent is non-zero; the request is flagged to
|
||||
* modify the parent resource too. */
|
||||
static int submit_test(const char *lockpath, int lockdepth,
|
||||
const char *reqpath, int reqdepth,
|
||||
int modparent)
|
||||
{
|
||||
ne_lock_store *store = ne_lockstore_create();
|
||||
ne_session *sess;
|
||||
struct ne_lock *lk = ne_lock_create();
|
||||
char *expect_if;
|
||||
int ret;
|
||||
|
||||
expect_if = ne_concat("<http://localhost:7777", lockpath,
|
||||
"> (<somelocktoken>)", NULL);
|
||||
CALL(make_session(&sess, serve_verify_if, expect_if));
|
||||
ne_free(expect_if);
|
||||
|
||||
ne_fill_server_uri(sess, &lk->uri);
|
||||
lk->uri.path = ne_strdup(lockpath);
|
||||
lk->token = ne_strdup("somelocktoken");
|
||||
lk->depth = lockdepth;
|
||||
|
||||
/* register the lock store, and add our lock for "/foo" to it. */
|
||||
ne_lockstore_register(store, sess);
|
||||
ne_lockstore_add(store, lk);
|
||||
|
||||
ret = do_request(sess, reqpath, reqdepth, modparent);
|
||||
CALL(await_server());
|
||||
|
||||
ne_lockstore_destroy(store);
|
||||
ne_session_destroy(sess);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int if_simple(void)
|
||||
{
|
||||
return submit_test("/foo", 0, "/foo", 0, 0);
|
||||
}
|
||||
|
||||
static int if_under_infinite(void)
|
||||
{
|
||||
return submit_test("/foo", NE_DEPTH_INFINITE, "/foo/bar", 0, 0);
|
||||
}
|
||||
|
||||
static int if_infinite_over(void)
|
||||
{
|
||||
return submit_test("/foo/bar", 0, "/foo/", NE_DEPTH_INFINITE, 0);
|
||||
}
|
||||
|
||||
static int if_child(void)
|
||||
{
|
||||
return submit_test("/foo/", 0, "/foo/bar", 0, 1);
|
||||
}
|
||||
|
||||
/* this is a special test, where the PARENT resource of "/foo/bar" is
|
||||
* modified, but NOT "/foo/bar" itself. An UNLOCK request on a
|
||||
* lock-null resource can do this; see ne_unlock() for the comment.
|
||||
* Regression test for neon <= 0.19.3, which didn't handle this
|
||||
* correctly. */
|
||||
static int if_covered_child(void)
|
||||
{
|
||||
return submit_test("/", NE_DEPTH_INFINITE, "/foo/bar", -1, 1);
|
||||
}
|
||||
|
||||
static int serve_discovery(ne_socket *sock, void *userdata)
|
||||
{
|
||||
char buf[BUFSIZ], *resp = userdata;
|
||||
|
||||
ON(discard_request(sock));
|
||||
ONN("no PROPFIND body", clength == 0);
|
||||
ON(ne_sock_read(sock, buf, clength) < 0);
|
||||
ON(SEND_STRING(sock, "HTTP/1.0 207 OK" EOL
|
||||
"Connection: close" EOL EOL));
|
||||
ON(SEND_STRING(sock, resp));
|
||||
return OK;
|
||||
}
|
||||
|
||||
struct result_args {
|
||||
struct ne_lock *lock;
|
||||
int result;
|
||||
};
|
||||
|
||||
static int lock_compare(const char *ctx,
|
||||
const struct ne_lock *a, const struct ne_lock *b)
|
||||
{
|
||||
ONV(!a->uri.host || !a->uri.scheme || !a->uri.path,
|
||||
("URI structure incomplete in %s", ctx));
|
||||
ONV(ne_uri_cmp(&a->uri, &b->uri) != 0,
|
||||
("URI comparison failed for %s: %s not %s", ctx,
|
||||
ne_uri_unparse(&a->uri), ne_uri_unparse(&b->uri)));
|
||||
ONV(a->depth != b->depth,
|
||||
("%s depth was %d not %d", ctx, a->depth, b->depth));
|
||||
ONV(a->scope != b->scope,
|
||||
("%s scope was %d not %d", ctx, a->scope, b->scope));
|
||||
ONV(a->type != b->type,
|
||||
("%s type was %d not %d", ctx, a->type, b->type));
|
||||
return OK;
|
||||
}
|
||||
|
||||
static void discover_result(void *userdata, const struct ne_lock *lk,
|
||||
const char *path, const ne_status *st)
|
||||
{
|
||||
struct result_args *args = userdata;
|
||||
args->result = lock_compare("discovered lock", lk, args->lock);
|
||||
}
|
||||
|
||||
static int discover(void)
|
||||
{
|
||||
ne_session *sess = ne_session_create("http", "localhost", 7777);
|
||||
char *response;
|
||||
int ret;
|
||||
struct result_args args;
|
||||
|
||||
args.lock = ne_lock_create();
|
||||
|
||||
args.lock->owner = ne_strdup("someowner");
|
||||
args.lock->token = ne_strdup("sometoken");
|
||||
|
||||
/* default */
|
||||
args.result = FAIL;
|
||||
t_context("results callback never invoked");
|
||||
|
||||
ne_fill_server_uri(sess, &args.lock->uri);
|
||||
args.lock->uri.path = ne_strdup("/lockme");
|
||||
|
||||
response = discover_response("/lockme", args.lock);
|
||||
CALL(spawn_server(7777, serve_discovery, response));
|
||||
|
||||
ret = ne_lock_discover(sess, "/lockme", discover_result, &args);
|
||||
CALL(await_server());
|
||||
ONREQ(ret);
|
||||
|
||||
ne_lock_destroy(args.lock);
|
||||
ne_session_destroy(sess);
|
||||
|
||||
return args.result;
|
||||
}
|
||||
|
||||
/* Check that the token for the response header */
|
||||
static int lock_shared(void)
|
||||
{
|
||||
ne_session *sess;
|
||||
char *resp, *rbody;
|
||||
struct ne_lock *lock, *resplocks[3];
|
||||
|
||||
#define FILLK(l, s) do { \
|
||||
(l)->token = strdup("opaquelocktoken:" s); \
|
||||
(l)->owner = strdup("owner " s); \
|
||||
(l)->uri.path = strdup("/" s); (l)->uri.host = strdup("localhost"); \
|
||||
(l)->uri.scheme = strdup("http"); (l)->uri.port = 7777; } while (0)
|
||||
|
||||
resplocks[0] = ne_lock_create();
|
||||
resplocks[1] = ne_lock_create();
|
||||
resplocks[2] = NULL;
|
||||
FILLK(resplocks[0], "alpha");
|
||||
FILLK(resplocks[1], "beta");
|
||||
resplocks[0]->timeout = 100;
|
||||
resplocks[1]->timeout = 200;
|
||||
|
||||
rbody = multi_lock_response(resplocks);
|
||||
|
||||
resp = ne_concat("HTTP/1.1 200 OK\r\n" "Server: neon-test-server\r\n"
|
||||
"Lock-Token: <opaquelocktoken:beta>" EOL
|
||||
"Connection: close\r\n\r\n", rbody, NULL);
|
||||
ne_free(rbody);
|
||||
|
||||
CALL(make_session(&sess, single_serve_string, resp));
|
||||
ne_free(resp);
|
||||
|
||||
lock = ne_lock_create();
|
||||
ne_fill_server_uri(sess, &lock->uri);
|
||||
lock->uri.path = ne_strdup("/beta");
|
||||
|
||||
ONREQ(ne_lock(sess, lock));
|
||||
|
||||
CALL(await_server());
|
||||
|
||||
CALL(lock_compare("returned lock", resplocks[1], lock));
|
||||
|
||||
ne_session_destroy(sess);
|
||||
ne_lock_destroy(lock);
|
||||
ne_lock_destroy(resplocks[0]);
|
||||
ne_lock_destroy(resplocks[1]);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static void dummy_discover(void *userdata, const struct ne_lock *lock,
|
||||
const char *uri, const ne_status *status)
|
||||
{
|
||||
}
|
||||
|
||||
/* This failed with neon 0.23.x and earlier when memory leak detection
|
||||
* is enabled. */
|
||||
static int fail_discover(void)
|
||||
{
|
||||
ne_session *sess;
|
||||
int ret;
|
||||
|
||||
CALL(make_session(&sess, single_serve_string,
|
||||
"HTTP/1.0 207 OK\r\n" "Connection: close\r\n" "\r\n"
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
|
||||
"<D:multistatus xmlns:D='DAV:'>\n"
|
||||
"<D:response><D:href>/foo/bar</D:href><D:propstat>\n"
|
||||
"</parse this, my friend>\n"));
|
||||
|
||||
ret = ne_lock_discover(sess, "/foo", dummy_discover, NULL);
|
||||
CALL(await_server());
|
||||
|
||||
ONN("discovery okay for response with invalid XML!?", ret != NE_ERROR);
|
||||
|
||||
ne_session_destroy(sess);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int no_creds(void *ud, const char *realm, int attempt,
|
||||
char *username, char *password)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static int fail_lockauth(void)
|
||||
{
|
||||
ne_session *sess;
|
||||
struct ne_lock *lock;
|
||||
int ret;
|
||||
|
||||
CALL(make_session(&sess, single_serve_string,
|
||||
"HTTP/1.1 401 Auth Denied\r\n"
|
||||
"WWW-Authenticate: Basic realm=\"realm@host\"\r\n"
|
||||
"Content-Length: 0\r\n"
|
||||
"\r\n"
|
||||
"HTTP/1.1 401 Auth Denied\r\n"
|
||||
"WWW-Authenticate: Basic realm=\"realm@host\"\r\n"
|
||||
"Connection: close\r\n"
|
||||
"\r\n"));
|
||||
|
||||
ne_set_server_auth(sess, no_creds, NULL);
|
||||
|
||||
lock = make_lock("/foo", NULL, ne_lockscope_exclusive, NE_DEPTH_ZERO);
|
||||
ret = ne_lock(sess, lock);
|
||||
ONV(ret != NE_AUTH,
|
||||
("attempt to lock did not fail with NE_AUTH: %d (%s)",
|
||||
ret, ne_get_error(sess)));
|
||||
ne_lock_destroy(lock);
|
||||
|
||||
lock = make_lock("/bar", "fish", ne_lockscope_exclusive, NE_DEPTH_ZERO);
|
||||
ret = ne_unlock(sess, lock);
|
||||
ONV(ret != NE_AUTH,
|
||||
("attempt to unlock did not fail with NE_AUTH: %d (%s)",
|
||||
ret, ne_get_error(sess)));
|
||||
ne_lock_destroy(lock);
|
||||
|
||||
|
||||
CALL(await_server());
|
||||
ne_session_destroy(sess);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
ne_test tests[] = {
|
||||
T(lookup_localhost),
|
||||
T(store_single),
|
||||
T(store_several),
|
||||
T(if_simple),
|
||||
T(if_under_infinite),
|
||||
T(if_infinite_over),
|
||||
T(if_child),
|
||||
T(if_covered_child),
|
||||
T(lock_timeout),
|
||||
T(lock_shared),
|
||||
T(discover),
|
||||
T_XLEAKY(fail_discover),
|
||||
T(fail_lockauth),
|
||||
T(NULL)
|
||||
};
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
#!/bin/sh
|
||||
# Helper script to create CA and server certificates.
|
||||
|
||||
srcdir=${1-.}
|
||||
|
||||
OPENSSL=${OPENSSL-openssl}
|
||||
CONF=${srcdir}/openssl.conf
|
||||
REQ="${OPENSSL} req -config ${CONF}"
|
||||
CA="${OPENSSL} ca -config ${CONF} -batch"
|
||||
# MKCERT makes a self-signed cert
|
||||
MKCERT="${REQ} -x509 -new -days 900"
|
||||
|
||||
REQDN=reqDN
|
||||
STRMASK=default
|
||||
export REQDN STRMASK
|
||||
|
||||
set -ex
|
||||
|
||||
mkdir ca
|
||||
touch ca/index.txt
|
||||
echo 01 > ca/serial
|
||||
|
||||
${OPENSSL} genrsa -rand ${srcdir}/../configure > ca/key.pem
|
||||
${OPENSSL} genrsa -rand ${srcdir}/../configure > client.key
|
||||
|
||||
${MKCERT} -key ca/key.pem -out ca/cert.pem <<EOF
|
||||
US
|
||||
California
|
||||
Oakland
|
||||
Neosign
|
||||
Random Dept
|
||||
nowhere.example.com
|
||||
neon@webdav.org
|
||||
EOF
|
||||
|
||||
# Function to generate appropriate output for `openssl req'.
|
||||
csr_fields() {
|
||||
CN=${2-"localhost"}
|
||||
OU=${1-"Neon QA Dept"}
|
||||
Org=${3-"Neon Hackers Ltd"}
|
||||
Locality=${4-"Cambridge"}
|
||||
State=${5-"Cambridgeshire"}
|
||||
cat <<EOF
|
||||
GB
|
||||
${State}
|
||||
${Locality}
|
||||
${Org}
|
||||
${OU}
|
||||
${CN}
|
||||
neon@webdav.org
|
||||
.
|
||||
.
|
||||
EOF
|
||||
}
|
||||
|
||||
csr_fields | ${REQ} -new -key ${srcdir}/server.key -out server.csr
|
||||
|
||||
csr_fields "Upper Case Dept" lOcALhost | \
|
||||
${REQ} -new -key ${srcdir}/server.key -out caseless.csr
|
||||
|
||||
csr_fields "Use AltName Dept" nowhere.example.com | \
|
||||
${REQ} -new -key ${srcdir}/server.key -out altname1.csr
|
||||
|
||||
csr_fields "Two AltName Dept" nowhere.example.com | \
|
||||
${REQ} -new -key ${srcdir}/server.key -out altname2.csr
|
||||
|
||||
csr_fields "Third AltName Dept" nowhere.example.com | \
|
||||
${REQ} -new -key ${srcdir}/server.key -out altname3.csr
|
||||
|
||||
csr_fields "Fourth AltName Dept" localhost | \
|
||||
${REQ} -new -key ${srcdir}/server.key -out altname4.csr
|
||||
|
||||
csr_fields "Good ipAddress altname Dept" nowhere.example.com | \
|
||||
${REQ} -new -key ${srcdir}/server.key -out altname5.csr
|
||||
|
||||
csr_fields "Bad ipAddress altname Dept" nowhere.example.com | \
|
||||
${REQ} -new -key ${srcdir}/server.key -out altname6.csr
|
||||
|
||||
csr_fields "Self-Signed" | \
|
||||
${MKCERT} -key ${srcdir}/server.key -out ssigned.pem
|
||||
|
||||
csr_fields "Bad Hostname Department" nohost.example.com | \
|
||||
${MKCERT} -key ${srcdir}/server.key -out wrongcn.pem
|
||||
|
||||
# default => T61String
|
||||
csr_fields "`echo -e 'H\0350llo World'`" localhost |
|
||||
${MKCERT} -key ${srcdir}/server.key -out t61subj.cert
|
||||
|
||||
STRMASK=pkix # => BMPString
|
||||
csr_fields "`echo -e 'H\0350llo World'`" localhost |
|
||||
${MKCERT} -key ${srcdir}/server.key -out bmpsubj.cert
|
||||
|
||||
STRMASK=utf8only # => UTF8String
|
||||
csr_fields "`echo -e 'H\0350llo World'`" localhost |
|
||||
${MKCERT} -key ${srcdir}/server.key -out utf8subj.cert
|
||||
|
||||
STRMASK=default
|
||||
|
||||
### produce a set of CA certs
|
||||
|
||||
csr_fields "First Random CA" "first.example.com" "CAs Ltd." Lincoln Lincolnshire | \
|
||||
${MKCERT} -key ${srcdir}/server.key -out ca1.pem
|
||||
|
||||
csr_fields "Second Random CA" "second.example.com" "CAs Ltd." Falmouth Cornwall | \
|
||||
${MKCERT} -key ${srcdir}/server.key -out ca2.pem
|
||||
|
||||
csr_fields "Third Random CA" "third.example.com" "CAs Ltd." Ipswich Suffolk | \
|
||||
${MKCERT} -key ${srcdir}/server.key -out ca3.pem
|
||||
|
||||
csr_fields "Fourth Random CA" "fourth.example.com" "CAs Ltd." Norwich Norfolk | \
|
||||
${MKCERT} -key ${srcdir}/server.key -out ca4.pem
|
||||
|
||||
cat ca[1234].pem > calist.pem
|
||||
|
||||
# The hostname munging only works if `hostname` always reports the
|
||||
# FQDN; continue if anything fails since appropriate tests will be
|
||||
# skipped if this fails.
|
||||
|
||||
hostname=`hostname | sed 's,\..*,,'` || true
|
||||
domain=`hostname | sed 's,.*\.,,'` || true
|
||||
fqdn=`hostname` || true
|
||||
if [ "x${hostname}.${domain}" = "x${fqdn}" ]; then
|
||||
csr_fields "Wildcard Cert Dept" "*.${domain}" | \
|
||||
${REQ} -new -key ${srcdir}/server.key -out wildcard.csr
|
||||
${CA} -days 900 -in wildcard.csr -out wildcard.cert
|
||||
fi
|
||||
|
||||
csr_fields "Neon Client Cert" ignored.example.com | \
|
||||
${REQ} -new -key client.key -out client.csr
|
||||
|
||||
### requests using special DN.
|
||||
|
||||
REQDN=reqDN.doubleCN
|
||||
csr_fields "Double CN Dept" "nohost.example.com
|
||||
localhost" | ${REQ} -new -key ${srcdir}/server.key -out twocn.csr
|
||||
|
||||
REQDN=reqDN.CNfirst
|
||||
echo localhost | ${REQ} -new -key ${srcdir}/server.key -out cnfirst.csr
|
||||
|
||||
REQDN=reqDN.missingCN
|
||||
echo GB | ${REQ} -new -key ${srcdir}/server.key -out missingcn.csr
|
||||
|
||||
REQDN=reqDN.justEmail
|
||||
echo blah@example.com | ${REQ} -new -key ${srcdir}/server.key -out justmail.csr
|
||||
|
||||
# presume AVAs will come out in least->most specific order still...
|
||||
REQDN=reqDN.twoOU
|
||||
csr_fields "Second OU Dept
|
||||
First OU Dept" | ${REQ} -new -key ${srcdir}/server.key -out twoou.csr
|
||||
|
||||
### don't put ${REQ} invocations after here
|
||||
|
||||
for f in server client twocn caseless cnfirst missingcn justmail twoou; do
|
||||
${CA} -days 900 -in ${f}.csr -out ${f}.cert
|
||||
done
|
||||
|
||||
for n in 1 2 3 4 5 6; do
|
||||
${CA} -extensions altExt${n} -days 900 \
|
||||
-in altname${n}.csr -out altname${n}.cert
|
||||
done
|
||||
|
||||
MKPKCS12="${OPENSSL} pkcs12 -export -passout stdin -in client.cert -inkey client.key"
|
||||
|
||||
# generate a PKCS12 cert from the client cert: -passOUT because it's the
|
||||
# passphrase on the OUTPUT cert, confusing...
|
||||
echo foobar | ${MKPKCS12} -name "Just A Neon Client Cert" -out client.p12
|
||||
|
||||
# generate a PKCS#12 cert with no password and a friendly name
|
||||
echo | ${MKPKCS12} -name "An Unencrypted Neon Client Cert" -out unclient.p12
|
||||
|
||||
# generate a PKCS#12 cert with no friendly name
|
||||
echo | ${MKPKCS12} -out noclient.p12
|
||||
|
||||
### a file containing a complete chain
|
||||
|
||||
cat ca/cert.pem server.cert > chain.pem
|
||||
@@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDODCCAuKgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBoTELMAkGA1UEBhMCR0Ix
|
||||
FzAVBgNVBAgTDkNhbWJyaWRnZXNoaXJlMRIwEAYDVQQHEwlDYW1icmlkZ2UxGjAY
|
||||
BgNVBAoTEU5lb24gSGFja2VycyBMdGQuMRUwEwYDVQQLEwxOZW9uIFFBIERlcHQx
|
||||
EjAQBgNVBAMTCWxvY2FsaG9zdDEeMBwGCSqGSIb3DQEJARYPbmVvbkB3ZWJkYXYu
|
||||
b3JnMB4XDTIzMTIyNzIwNDAyOVoXDTIzMTIyODIwNDAyOVowgaExCzAJBgNVBAYT
|
||||
AkdCMRcwFQYDVQQIEw5DYW1icmlkZ2VzaGlyZTESMBAGA1UEBxMJQ2FtYnJpZGdl
|
||||
MRowGAYDVQQKExFOZW9uIEhhY2tlcnMgTHRkLjEVMBMGA1UECxMMTmVvbiBRQSBE
|
||||
ZXB0MRIwEAYDVQQDEwlsb2NhbGhvc3QxHjAcBgkqhkiG9w0BCQEWD25lb25Ad2Vi
|
||||
ZGF2Lm9yZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDzRU5sZ8+CWQPvPkqJw9Kl
|
||||
oEgT2FqzZR9RT/qbJuRBmRphiRr0g7JOh5Mr7LXaKShedFLhGidutyKKwIZJnRht
|
||||
AgMBAAGjggEBMIH+MB0GA1UdDgQWBBRFA3ktzHSuD9uB6mJOWoElmOtknzCBzgYD
|
||||
VR0jBIHGMIHDgBRFA3ktzHSuD9uB6mJOWoElmOtkn6GBp6SBpDCBoTELMAkGA1UE
|
||||
BhMCR0IxFzAVBgNVBAgTDkNhbWJyaWRnZXNoaXJlMRIwEAYDVQQHEwlDYW1icmlk
|
||||
Z2UxGjAYBgNVBAoTEU5lb24gSGFja2VycyBMdGQuMRUwEwYDVQQLEwxOZW9uIFFB
|
||||
IERlcHQxEjAQBgNVBAMTCWxvY2FsaG9zdDEeMBwGCSqGSIb3DQEJARYPbmVvbkB3
|
||||
ZWJkYXYub3JnggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADQQA80TYV
|
||||
2F4QLveuldmxGoIOq5hHGxCR6aVsdtm4PGY49R5/ObCAgdWw/JV/Tc448JAz5QvU
|
||||
ahr1x9kA4Vo5NZ4q
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,96 @@
|
||||
[ca]
|
||||
default_ca = neonca
|
||||
|
||||
[neonca]
|
||||
dir = ./ca
|
||||
database = $dir/index.txt
|
||||
new_certs_dir = $dir
|
||||
certificate = $dir/cert.pem
|
||||
serial = $dir/serial
|
||||
private_key = $dir/key.pem
|
||||
policy = policy_any
|
||||
default_md = md5
|
||||
x509_extensions = issuedExt
|
||||
|
||||
[policy_any]
|
||||
countryName = optional
|
||||
stateOrProvinceName = optional
|
||||
localityName = optional
|
||||
organizationName = optional
|
||||
organizationalUnitName = optional
|
||||
commonName = optional
|
||||
emailAddress = optional
|
||||
|
||||
[req]
|
||||
distinguished_name = $ENV::REQDN
|
||||
x509_extensions = caExt
|
||||
string_mask = $ENV::STRMASK
|
||||
|
||||
[caExt]
|
||||
basicConstraints = CA:true
|
||||
|
||||
[issuedExt]
|
||||
basicConstraints = CA:false
|
||||
|
||||
# subjectAltName extension sections
|
||||
[altExt1]
|
||||
subjectAltName = DNS:localhost
|
||||
|
||||
# 2+3: AltNames with multiple entries to test the matching logic
|
||||
[altExt2]
|
||||
subjectAltName = DNS:nohost.example.com, DNS:localhost
|
||||
|
||||
[altExt3]
|
||||
subjectAltName = DNS:localhost, DNS:nohost.example.com
|
||||
|
||||
# an AltName with no DNS entries; should use commonName instead for
|
||||
# identity check
|
||||
[altExt4]
|
||||
subjectAltName = email:neon@webdav.org
|
||||
|
||||
# an AltName with IP address
|
||||
[altExt5]
|
||||
subjectAltName = IP:127.0.0.1
|
||||
|
||||
# an AltName with a bad IP address
|
||||
[altExt6]
|
||||
subjectAltName = IP:1.2.3.4
|
||||
|
||||
[reqDN]
|
||||
countryName = Country Name
|
||||
stateOrProvinceName = State or Province Name
|
||||
localityName = Locality Name
|
||||
organizationName = Organization Name
|
||||
organizationalUnitName = Organizational Unit Name
|
||||
commonName = Common Name (eg, your name or your server\'s hostname)
|
||||
emailAddress = Email Address
|
||||
|
||||
# a DN which gives two commonName attributes.
|
||||
[reqDN.doubleCN]
|
||||
countryName = Country Name
|
||||
stateOrProvinceName = State or Province Name
|
||||
localityName = Locality Name
|
||||
organizationName = Organization Name
|
||||
organizationalUnitName = Organizational Unit Name
|
||||
0.commonName = Common Name
|
||||
1.commonName = Common Name
|
||||
emailAddress = Email Address
|
||||
|
||||
[reqDN.twoOU]
|
||||
countryName = Country Name
|
||||
stateOrProvinceName = State or Province Name
|
||||
localityName = Locality Name
|
||||
organizationName = Organization Name
|
||||
0.organizationalUnitName = Organizational Unit Name
|
||||
1.organizationalUnitName = Organizational Unit Name
|
||||
commonName = Common Name (eg, your name or your server\'s hostname)
|
||||
emailAddress = Email Address
|
||||
|
||||
[reqDN.CNfirst]
|
||||
commonName = Common Name
|
||||
|
||||
[reqDN.missingCN]
|
||||
countryName = CountryName
|
||||
|
||||
[reqDN.justEmail]
|
||||
emailAddress = CountryName
|
||||
@@ -0,0 +1,597 @@
|
||||
/*
|
||||
Tests for property handling
|
||||
Copyright (C) 2002-2004, Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "ne_props.h"
|
||||
|
||||
#include "tests.h"
|
||||
#include "child.h"
|
||||
#include "utils.h"
|
||||
|
||||
static const ne_propname p_alpha = {"DAV:", "alpha"},
|
||||
p_beta = {"http://webdav.org/random/namespace", "beta"},
|
||||
p_delta = {NULL, "delta"};
|
||||
|
||||
/* Tests little except that ne_proppatch() doesn't segfault. */
|
||||
static int patch_simple(void)
|
||||
{
|
||||
ne_session *sess;
|
||||
ne_proppatch_operation ops[] = {
|
||||
{ &p_alpha, ne_propset, "fish" },
|
||||
{ &p_beta, ne_propremove, NULL },
|
||||
{ NULL, ne_propset, NULL }
|
||||
};
|
||||
|
||||
CALL(make_session(&sess, single_serve_string,
|
||||
"HTTP/1.1 200 Goferit\r\n"
|
||||
"Connection: close\r\n\r\n"));
|
||||
ONREQ(ne_proppatch(sess, "/fish", ops));
|
||||
ne_session_destroy(sess);
|
||||
return await_server();
|
||||
}
|
||||
|
||||
#define RESP207 "HTTP/1.0 207 Stuff\r\n" "Server: foo\r\n\r\n"
|
||||
|
||||
static void dummy_results(void *ud, const char *href,
|
||||
const ne_prop_result_set *rset)
|
||||
{
|
||||
NE_DEBUG(NE_DBG_HTTP, "dummy_results.\n");
|
||||
}
|
||||
|
||||
/* Regression tests for propfind bodies which caused segfaults. */
|
||||
static int regress(void)
|
||||
{
|
||||
static const char *bodies[] = {
|
||||
RESP207 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
|
||||
"<multistatus xmlns=\"DAV:\">"
|
||||
"<response><propstat><prop><href>"
|
||||
"</href></prop></propstat></response>"
|
||||
"</multistatus>",
|
||||
|
||||
/* segfaults with neon <= 0.23.5 */
|
||||
RESP207 "<?xml version=\"1.0\"?><D:multistatus xmlns:D=\"DAV:\">"
|
||||
"<D:response><D:href>/foo/</D:href>"
|
||||
"<D:propstat/>"
|
||||
"<D:status>HTTP/1.1 404 Not Found</D:status>"
|
||||
"</D:multistatus>",
|
||||
|
||||
/* format string handling with neon <= 0.24.4 */
|
||||
RESP207 "<?xml version=\"1.0\"?><D:multistatus xmlns:D=\"DAV:\">"
|
||||
"<D:response><D:href>/foo/</D:href>"
|
||||
"<D:propstat/>"
|
||||
"<D:status>%s%s%s%s</D:status>"
|
||||
"</D:response></D:multistatus>",
|
||||
|
||||
NULL,
|
||||
};
|
||||
ne_session *sess;
|
||||
int n;
|
||||
|
||||
for (n = 0; bodies[n] != NULL; n++) {
|
||||
CALL(make_session(&sess, single_serve_string, (void *)bodies[n]));
|
||||
ne_simple_propfind(sess, "/", 0, NULL, dummy_results, NULL);
|
||||
ne_session_destroy(sess);
|
||||
CALL(await_server());
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int patch_regress(void)
|
||||
{
|
||||
static const char *bodies[] = {
|
||||
/* format string handling bugs with neon <= 0.24.4 */
|
||||
RESP207 "<?xml version=\"1.0\"?><D:multistatus xmlns:D=\"DAV:\">"
|
||||
"<D:response><D:href>/foo/</D:href>"
|
||||
"<D:status>HTTP/1.1 500 Bad Voodoo</D:status>"
|
||||
"<D:responsedescription>%s%s%s%s</D:responsedescription>"
|
||||
"</D:response></D:multistatus>",
|
||||
|
||||
RESP207 "<?xml version=\"1.0\"?><D:multistatus xmlns:D=\"DAV:\">"
|
||||
"<D:response><D:href>/foo/</D:href>"
|
||||
"<D:status>HTTP/1.1 %s%s%s%s</D:status>",
|
||||
|
||||
NULL
|
||||
};
|
||||
ne_session *sess;
|
||||
int n;
|
||||
static const ne_propname pn = { "DAV:", "foobar" };
|
||||
ne_proppatch_operation pops[] = {
|
||||
{ &pn, ne_propset, "fish" },
|
||||
{ NULL, ne_propset, NULL }
|
||||
};
|
||||
|
||||
for (n = 0; bodies[n] != NULL; n++) {
|
||||
CALL(make_session(&sess, single_serve_string, (void *)bodies[n]));
|
||||
ne_proppatch(sess, "/", pops);
|
||||
ne_session_destroy(sess);
|
||||
CALL(await_server());
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int pstat_count;
|
||||
|
||||
/* tos_*: set of 207 callbacks which serialize the data back into a
|
||||
* text stream, which can be easily checked for correctness. */
|
||||
static void *tos_startresp(void *buf, const char *href)
|
||||
{
|
||||
ne_buffer_concat(buf, "start-resp[", href, "];", NULL);
|
||||
pstat_count = 0;
|
||||
return ne_strdup(href);
|
||||
}
|
||||
|
||||
static void tos_status_descr(ne_buffer *buf, const ne_status *status,
|
||||
const char *description)
|
||||
{
|
||||
if (status) {
|
||||
char s[50];
|
||||
ne_snprintf(s, sizeof s, "-status={%d %s}", status->code,
|
||||
status->reason_phrase);
|
||||
ne_buffer_zappend(buf, s);
|
||||
}
|
||||
if (description)
|
||||
ne_buffer_concat(buf, "-descr={", description, "}", NULL);
|
||||
}
|
||||
|
||||
static void tos_endresp(void *buf, void *response,
|
||||
const ne_status *status, const char *description)
|
||||
{
|
||||
char *href = response;
|
||||
ne_buffer_concat(buf, "end-resp[", href, "]", NULL);
|
||||
ne_free(href);
|
||||
tos_status_descr(buf, status, description);
|
||||
ne_buffer_zappend(buf, ";");
|
||||
}
|
||||
|
||||
static void *tos_startpstat(void *buf, void *resphref)
|
||||
{
|
||||
char num[20], *href;
|
||||
sprintf(num, "-%d", ++pstat_count);
|
||||
href = ne_concat(resphref, num, NULL);
|
||||
ne_buffer_concat(buf, "start-pstat[", href, "];", NULL);
|
||||
return href;
|
||||
}
|
||||
|
||||
static void tos_endpstat(void *buf, void *href,
|
||||
const ne_status *status, const char *description)
|
||||
{
|
||||
ne_buffer_concat(buf, "end-pstat[", href, "]", NULL);
|
||||
tos_status_descr(buf, status, description);
|
||||
ne_buffer_zappend(buf, ";");
|
||||
ne_free(href);
|
||||
}
|
||||
|
||||
struct propctx {
|
||||
ne_207_parser *p207;
|
||||
ne_buffer *buf;
|
||||
};
|
||||
|
||||
#define STATE_myprop (NE_PROPS_STATE_TOP)
|
||||
|
||||
static int tos_startprop(void *userdata, int parent,
|
||||
const char *nspace, const char *name,
|
||||
const char **atts)
|
||||
{
|
||||
if (parent == NE_207_STATE_PROP &&
|
||||
strcmp(nspace, "DAV:") == 0 &&
|
||||
(strcmp(name, "propone") == 0 || strcmp(name, "proptwo") == 0)) {
|
||||
/* Handle this! */
|
||||
struct propctx *ctx = userdata;
|
||||
char *resphref = ne_207_get_current_response(ctx->p207);
|
||||
char *pstathref = ne_207_get_current_propstat(ctx->p207);
|
||||
|
||||
ne_buffer_concat(ctx->buf, "start-prop[", resphref, ",", pstathref,
|
||||
",", name, "];", NULL);
|
||||
|
||||
return STATE_myprop;
|
||||
} else {
|
||||
return NE_XML_DECLINE;
|
||||
}
|
||||
}
|
||||
|
||||
static int tos_cdata(void *userdata, int state,
|
||||
const char *cdata, size_t len)
|
||||
{
|
||||
struct propctx *ctx = userdata;
|
||||
|
||||
ne_buffer_zappend(ctx->buf, "cdata-prop[");
|
||||
ne_buffer_append(ctx->buf, cdata, len);
|
||||
ne_buffer_zappend(ctx->buf, "];");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tos_endprop(void *userdata, int state,
|
||||
const char *nspace, const char *name)
|
||||
{
|
||||
struct propctx *ctx = userdata;
|
||||
|
||||
ne_buffer_concat(ctx->buf, "end-prop[", name, "];", NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int run_207_response(char *resp, const char *expected)
|
||||
{
|
||||
ne_buffer *buf = ne_buffer_create();
|
||||
ne_session *sess = ne_session_create("http", "localhost", 7777);
|
||||
ne_xml_parser *p = ne_xml_create();
|
||||
ne_207_parser *p207 = ne_207_create(p, buf);
|
||||
ne_request *req = ne_request_create(sess, "PROPFIND", "/foo");
|
||||
struct propctx ctx;
|
||||
|
||||
ne_add_response_body_reader(req, ne_accept_207, ne_xml_parse_v, p);
|
||||
|
||||
ne_207_set_response_handlers(p207, tos_startresp, tos_endresp);
|
||||
ne_207_set_propstat_handlers(p207, tos_startpstat, tos_endpstat);
|
||||
|
||||
ctx.buf = buf;
|
||||
ctx.p207 = p207;
|
||||
ne_xml_push_handler(p, tos_startprop, tos_cdata, tos_endprop, &ctx);
|
||||
|
||||
CALL(spawn_server(7777, single_serve_string, resp));
|
||||
|
||||
ONREQ(ne_request_dispatch(req));
|
||||
|
||||
CALL(await_server());
|
||||
|
||||
ONV(ne_xml_failed(p),
|
||||
("parse error in response body: %s", ne_xml_get_error(p)));
|
||||
|
||||
ONV(strcmp(buf->data, expected),
|
||||
("comparison failed.\n"
|
||||
"expected string: `%s'\n"
|
||||
"got string: `%s'", expected, buf->data));
|
||||
|
||||
ne_buffer_destroy(buf);
|
||||
ne_207_destroy(p207);
|
||||
ne_xml_destroy(p);
|
||||
ne_request_destroy(req);
|
||||
ne_session_destroy(sess);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* Macros for easily writing a 207 response body; all expand to
|
||||
* a string literal. */
|
||||
#define MULTI_207(x) "HTTP/1.0 207 Foo\r\nConnection: close\r\n\r\n" \
|
||||
"<?xml version='1.0'?>\r\n" \
|
||||
"<D:multistatus xmlns:D='DAV:'>" x "</D:multistatus>"
|
||||
#define RESP_207(href, x) "<D:response><D:href>" href "</D:href>" x \
|
||||
"</D:response>"
|
||||
#define PSTAT_207(x) "<D:propstat>" x "</D:propstat>"
|
||||
#define STAT_207(s) "<D:status>HTTP/1.1 " s "</D:status>"
|
||||
#define DESCR_207(d) "<D:responsedescription>" d "</D:responsedescription>"
|
||||
#define DESCR_REM "The end of the world, as we know it"
|
||||
|
||||
#define PROPS_207(x) "<D:prop>" x "</D:prop>"
|
||||
#define APROP_207(n, c) "<D:" n ">" c "</D:" n ">"
|
||||
|
||||
/* Tests for the 207 interface: send a 207 response body, compare the
|
||||
* re-serialized string returned with that expected. */
|
||||
static int two_oh_seven(void)
|
||||
{
|
||||
static char *ts[][2] = {
|
||||
{ MULTI_207(RESP_207("/foo", "")),
|
||||
"start-resp[/foo];end-resp[/foo];" },
|
||||
|
||||
/* test for response status handling */
|
||||
{ MULTI_207(RESP_207("/bar", STAT_207("200 OK"))),
|
||||
"start-resp[/bar];end-resp[/bar]-status={200 OK};" },
|
||||
|
||||
/* test that empty description == NULL description argument */
|
||||
{ MULTI_207(RESP_207("/bar", STAT_207("200 OK") DESCR_207(""))),
|
||||
"start-resp[/bar];end-resp[/bar]-status={200 OK};" },
|
||||
|
||||
/* test multiple responses */
|
||||
{ MULTI_207(RESP_207("/hello/world", STAT_207("200 OK"))
|
||||
RESP_207("/foo/bar", STAT_207("999 French Fries"))),
|
||||
"start-resp[/hello/world];end-resp[/hello/world]-status={200 OK};"
|
||||
"start-resp[/foo/bar];end-resp[/foo/bar]"
|
||||
"-status={999 French Fries};"
|
||||
},
|
||||
|
||||
/* test multiple propstats in mulitple responses */
|
||||
{ MULTI_207(RESP_207("/al/pha",
|
||||
PSTAT_207(STAT_207("321 Une"))
|
||||
PSTAT_207(STAT_207("432 Deux"))
|
||||
PSTAT_207(STAT_207("543 Trois")))
|
||||
RESP_207("/be/ta",
|
||||
PSTAT_207(STAT_207("787 Quatre"))
|
||||
PSTAT_207(STAT_207("878 Cinq")))),
|
||||
"start-resp[/al/pha];"
|
||||
"start-pstat[/al/pha-1];end-pstat[/al/pha-1]-status={321 Une};"
|
||||
"start-pstat[/al/pha-2];end-pstat[/al/pha-2]-status={432 Deux};"
|
||||
"start-pstat[/al/pha-3];end-pstat[/al/pha-3]-status={543 Trois};"
|
||||
"end-resp[/al/pha];"
|
||||
"start-resp[/be/ta];"
|
||||
"start-pstat[/be/ta-1];end-pstat[/be/ta-1]-status={787 Quatre};"
|
||||
"start-pstat[/be/ta-2];end-pstat[/be/ta-2]-status={878 Cinq};"
|
||||
"end-resp[/be/ta];"
|
||||
},
|
||||
|
||||
/* test that incomplete responses are completely ignored. */
|
||||
{ MULTI_207("<D:response/>"
|
||||
RESP_207("/", STAT_207("123 Hoorah"))
|
||||
"<D:response/>"
|
||||
"<D:response><D:propstat>hello</D:propstat></D:response>"
|
||||
"<D:response><D:href/></D:response>"
|
||||
RESP_207("/bar", STAT_207("200 OK"))),
|
||||
"start-resp[/];end-resp[/]-status={123 Hoorah};"
|
||||
"start-resp[/bar];end-resp[/bar]-status={200 OK};" },
|
||||
|
||||
/* tests for propstat status */
|
||||
{ MULTI_207(RESP_207("/pstat",
|
||||
PSTAT_207("<D:prop/>" STAT_207("666 Doomed")))),
|
||||
"start-resp[/pstat];start-pstat[/pstat-1];"
|
||||
"end-pstat[/pstat-1]-status={666 Doomed};end-resp[/pstat];" },
|
||||
|
||||
{ MULTI_207(RESP_207("/pstat", PSTAT_207("<D:status/>"))),
|
||||
"start-resp[/pstat];start-pstat[/pstat-1];"
|
||||
"end-pstat[/pstat-1];end-resp[/pstat];" },
|
||||
|
||||
/* tests for responsedescription handling */
|
||||
{ MULTI_207(RESP_207("/bar", STAT_207("200 OK") DESCR_207(DESCR_REM))),
|
||||
"start-resp[/bar];end-resp[/bar]-status={200 OK}"
|
||||
"-descr={" DESCR_REM "};" },
|
||||
|
||||
{ MULTI_207(RESP_207("/bar",
|
||||
PSTAT_207(STAT_207("456 Too Hungry")
|
||||
DESCR_207("Not enough food available"))
|
||||
STAT_207("200 OK") DESCR_207("Not " DESCR_REM))),
|
||||
"start-resp[/bar];"
|
||||
"start-pstat[/bar-1];end-pstat[/bar-1]-status={456 Too Hungry}"
|
||||
"-descr={Not enough food available};"
|
||||
"end-resp[/bar]-status={200 OK}-descr={Not " DESCR_REM "};" },
|
||||
|
||||
/* intermingle some random elements and cdata to make sure
|
||||
* they are ignored. */
|
||||
{ MULTI_207("<D:fish-food/>blargl"
|
||||
RESP_207("/b<ping-pong/>ar", "<D:sausages/>"
|
||||
PSTAT_207("<D:hello-mum/>blergl")
|
||||
STAT_207("200 <pong-ping/> OK") "foop"
|
||||
DESCR_207(DESCR_REM) "carroon")
|
||||
"carapi"),
|
||||
"start-resp[/bar];start-pstat[/bar-1];end-pstat[/bar-1];"
|
||||
"end-resp[/bar]-status={200 OK}-descr={" DESCR_REM "};" },
|
||||
|
||||
/* test for properties within a 207. */
|
||||
{ MULTI_207(RESP_207("/alpha",
|
||||
PSTAT_207(PROPS_207(
|
||||
APROP_207("propone", "hello")
|
||||
APROP_207("proptwo", "foobar"))
|
||||
STAT_207("200 OK")))),
|
||||
"start-resp[/alpha];start-pstat[/alpha-1];"
|
||||
"start-prop[/alpha,/alpha-1,propone];cdata-prop[hello];"
|
||||
"end-prop[propone];"
|
||||
"start-prop[/alpha,/alpha-1,proptwo];cdata-prop[foobar];"
|
||||
"end-prop[proptwo];"
|
||||
"end-pstat[/alpha-1]-status={200 OK};end-resp[/alpha];" }
|
||||
|
||||
};
|
||||
size_t n;
|
||||
|
||||
for (n = 0; n < sizeof(ts)/sizeof(ts[0]); n++)
|
||||
CALL(run_207_response(ts[n][0], ts[n][1]));
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* Serialize propfind result callbacks into a string */
|
||||
static int simple_iterator(void *buf, const ne_propname *name,
|
||||
const char *value, const ne_status *st)
|
||||
{
|
||||
char code[20];
|
||||
ne_buffer_concat(buf, "prop:[{", name->nspace, ",",
|
||||
name->name, "}=", NULL);
|
||||
if (value)
|
||||
ne_buffer_concat(buf, "'", value, "'", NULL);
|
||||
else
|
||||
ne_buffer_zappend(buf, "#novalue#");
|
||||
sprintf(code, ":{%d ", st->code);
|
||||
if (st->reason_phrase)
|
||||
ne_buffer_concat(buf, code, st->reason_phrase, "}];", NULL);
|
||||
else
|
||||
ne_buffer_concat(buf, code, "#noreason#}];", NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void simple_results(void *buf, const char *href,
|
||||
const ne_prop_result_set *rset)
|
||||
{
|
||||
ne_buffer_concat(buf, "results(", href, ",", NULL);
|
||||
ne_propset_iterate(rset, simple_iterator, buf);
|
||||
ne_buffer_zappend(buf, ")//");
|
||||
}
|
||||
|
||||
static int diffcmp(const char *expected, const char *actual)
|
||||
{
|
||||
size_t n;
|
||||
|
||||
if (!strcmp(expected, actual)) return OK;
|
||||
|
||||
for (n = 0; expected[n] && actual[n]; n++) {
|
||||
if (expected[n] != actual[n]) {
|
||||
t_context("difference at byte %" NE_FMT_SIZE_T ": "
|
||||
"`%.6s...' not `%.6s...'",
|
||||
n, actual+n, expected+n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
|
||||
static int run_simple_propfind(const ne_propname *props, char *resp,
|
||||
int depth, const char *expected)
|
||||
{
|
||||
ne_session *sess = ne_session_create("http", "localhost", 7777);
|
||||
ne_buffer *buf = ne_buffer_create();
|
||||
|
||||
CALL(spawn_server(7777, single_serve_string, resp));
|
||||
|
||||
ONREQ(ne_simple_propfind(sess, "/propfind", depth, props,
|
||||
simple_results, buf));
|
||||
|
||||
CALL(await_server());
|
||||
|
||||
CALL(diffcmp(expected, buf->data));
|
||||
|
||||
ne_buffer_destroy(buf);
|
||||
ne_session_destroy(sess);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* a PROPFIND response body for the {DAV:}fishbone property, using
|
||||
* given property value and status. */
|
||||
#define FISHBONE_RESP(value, status) MULTI_207(RESP_207("/foop", \
|
||||
PSTAT_207(PROPS_207(APROP_207("fishbone", value)) \
|
||||
STAT_207(status))))
|
||||
|
||||
static int pfind_simple(void)
|
||||
{
|
||||
static const struct {
|
||||
char *resp;
|
||||
const char *expected;
|
||||
int depth, pset;
|
||||
} ts[] = {
|
||||
/* simple single property. */
|
||||
{ FISHBONE_RESP("hello, world", "212 Well OK"),
|
||||
"results(/foop,prop:[{DAV:,fishbone}='hello, world':{212 Well OK}];)//",
|
||||
0, 0 },
|
||||
/* property with some nested elements. */
|
||||
{ FISHBONE_RESP("this is <foo/> a property <bar><lemon>fish</lemon></bar> value",
|
||||
"299 Just About OK"),
|
||||
"results(/foop,prop:[{DAV:,fishbone}="
|
||||
"'this is <foo></foo> a property "
|
||||
"<bar><lemon>fish</lemon></bar> value':"
|
||||
"{299 Just About OK}];)//",
|
||||
0, 0 },
|
||||
|
||||
/* failed to fetch a property. */
|
||||
{ FISHBONE_RESP("property value is ignored",
|
||||
"404 Il n'ya pas de property"),
|
||||
"results(/foop,prop:[{DAV:,fishbone}=#novalue#:"
|
||||
"{404 Il n'ya pas de property}];)//",
|
||||
0, 0 },
|
||||
|
||||
#if 0
|
||||
/* propstat missing status should be ignored; if a response contains no
|
||||
* valid propstats, it should also be ignored. */
|
||||
{ MULTI_207(RESP_207("/alpha", PSTAT_207(APROP_207("fishbone", "unseen")))
|
||||
RESP_207("/beta", PSTAT_207(APROP_207("fishbone", "hello, world")
|
||||
STAT_207("200 OK")))),
|
||||
"results(/beta,prop:[{DAV:,fishbone}='hello, world':{200 OK}];)//", 0, 0},
|
||||
#endif
|
||||
|
||||
/* props on several resources */
|
||||
{ MULTI_207(RESP_207("/alpha",
|
||||
PSTAT_207(PROPS_207(APROP_207("fishbone", "strike one"))
|
||||
STAT_207("234 First is OK")))
|
||||
RESP_207("/beta",
|
||||
PSTAT_207(PROPS_207(APROP_207("fishbone", "strike two"))
|
||||
STAT_207("256 Second is OK")))),
|
||||
"results(/alpha,prop:[{DAV:,fishbone}='strike one':{234 First is OK}];)//"
|
||||
"results(/beta,prop:[{DAV:,fishbone}='strike two':{256 Second is OK}];)//",
|
||||
0, 0},
|
||||
|
||||
/* whitespace handling. */
|
||||
{ MULTI_207(RESP_207("\r\nhttp://localhost:7777/alpha ",
|
||||
PSTAT_207(PROPS_207(APROP_207("alpha", "beta"))
|
||||
"<D:status>\r\nHTTP/1.1 200 OK </D:status>"))),
|
||||
"results(http://localhost:7777/alpha,prop:[{DAV:,alpha}='beta':{200 OK}];)//",
|
||||
0, 0}
|
||||
};
|
||||
const ne_propname pset1[] = {
|
||||
{ "DAV:", "fishbone", },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
size_t n;
|
||||
|
||||
for (n = 0; n < sizeof(ts)/sizeof(ts[0]); n++) {
|
||||
const ne_propname *pset = pset1;
|
||||
|
||||
CALL(run_simple_propfind(pset, ts[n].resp, ts[n].depth,
|
||||
ts[n].expected));
|
||||
}
|
||||
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int unbounded_response(const char *header, const char *repeats)
|
||||
{
|
||||
ne_session *sess;
|
||||
struct infinite i = { header, repeats};
|
||||
int dbg;
|
||||
|
||||
CALL(make_session(&sess, serve_infinite, &i));
|
||||
|
||||
dbg = ne_debug_mask;
|
||||
|
||||
ONN("unbounded PROPFIND response did not fail",
|
||||
ne_simple_propfind(sess, "/", 0, NULL,
|
||||
dummy_results, NULL) != NE_ERROR);
|
||||
|
||||
CALL(reap_server());
|
||||
ne_session_destroy(sess);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int unbounded_propstats(void)
|
||||
{
|
||||
return unbounded_response(
|
||||
RESP207 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
|
||||
"<multistatus xmlns=\"DAV:\">"
|
||||
"<response><href>/</href>",
|
||||
"<propstat></propstat>");
|
||||
}
|
||||
|
||||
static int unbounded_props(void)
|
||||
{
|
||||
return unbounded_response(
|
||||
RESP207 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
|
||||
"<multistatus xmlns=\"DAV:\">"
|
||||
"<response><href>/</href><propstat>",
|
||||
"<prop><jim>hello, world</jim></prop>");
|
||||
}
|
||||
|
||||
ne_test tests[] = {
|
||||
T(two_oh_seven),
|
||||
T(patch_simple),
|
||||
T(pfind_simple),
|
||||
T(regress),
|
||||
T(patch_regress),
|
||||
T(unbounded_props),
|
||||
T(unbounded_propstats),
|
||||
T(NULL)
|
||||
};
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
Tests for 3xx redirect interface (ne_redirect.h)
|
||||
Copyright (C) 2002-2003, Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "ne_redirect.h"
|
||||
|
||||
#include "tests.h"
|
||||
#include "child.h"
|
||||
#include "utils.h"
|
||||
|
||||
struct redir_args {
|
||||
int code;
|
||||
const char *dest;
|
||||
const char *path;
|
||||
};
|
||||
|
||||
static int serve_redir(ne_socket *sock, void *ud)
|
||||
{
|
||||
struct redir_args *args = ud;
|
||||
char buf[BUFSIZ];
|
||||
|
||||
CALL(discard_request(sock));
|
||||
|
||||
ne_snprintf(buf, BUFSIZ,
|
||||
"HTTP/1.0 %d Get Ye Away\r\n"
|
||||
"Content-Length: 0\r\n"
|
||||
"Location: %s\r\n\n",
|
||||
args->code, args->dest);
|
||||
|
||||
SEND_STRING(sock, buf);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* Run a request to 'path' and retrieve the redirect destination to
|
||||
* *redir. */
|
||||
static int process_redir(ne_session *sess, const char *path,
|
||||
const ne_uri **redir)
|
||||
{
|
||||
ONN("did not get NE_REDIRECT", any_request(sess, path) != NE_REDIRECT);
|
||||
*redir = ne_redirect_location(sess);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int check_redir(struct redir_args *args, const char *expect)
|
||||
{
|
||||
ne_session *sess;
|
||||
const ne_uri *loc;
|
||||
char *unp;
|
||||
|
||||
CALL(make_session(&sess, serve_redir, args));
|
||||
ne_redirect_register(sess);
|
||||
|
||||
CALL(process_redir(sess, args->path, &loc));
|
||||
ONN("redirect location was NULL", loc == NULL);
|
||||
|
||||
unp = ne_uri_unparse(loc);
|
||||
ONV(strcmp(unp, expect), ("redirected to `%s' not `%s'", unp, expect));
|
||||
ne_free(unp);
|
||||
|
||||
ne_session_destroy(sess);
|
||||
CALL(await_server());
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
#define DEST "http://foo.com/blah/blah/bar"
|
||||
#define PATH "/redir/me"
|
||||
|
||||
static int simple(void)
|
||||
{
|
||||
struct redir_args args[] = {
|
||||
{301, DEST, PATH},
|
||||
{302, DEST, PATH},
|
||||
{303, DEST, PATH},
|
||||
{307, DEST, PATH},
|
||||
{0, NULL, NULL}
|
||||
};
|
||||
int n;
|
||||
|
||||
for (n = 0; args[n].code; n++)
|
||||
CALL(check_redir(&args[n], DEST));
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* check that a non-absoluteURI is qualified properly */
|
||||
static int non_absolute(void)
|
||||
{
|
||||
struct redir_args args = {302, "/foo/bar/blah", PATH};
|
||||
return check_redir(&args, "http://localhost:7777/foo/bar/blah");
|
||||
}
|
||||
|
||||
static int relative_1(void)
|
||||
{
|
||||
struct redir_args args = {302, "norman", "/foo/bar"};
|
||||
return check_redir(&args, "http://localhost:7777/foo/norman");
|
||||
}
|
||||
|
||||
static int relative_2(void)
|
||||
{
|
||||
struct redir_args args = {302, "wishbone", "/foo/bar/"};
|
||||
return check_redir(&args, "http://localhost:7777/foo/bar/wishbone");
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* could implement failure on self-referential redirects, but
|
||||
* realistically, the application must implement a max-redirs count
|
||||
* check, so it's kind of redundant. Mozilla takes this approach. */
|
||||
static int fail_loop(void)
|
||||
{
|
||||
ne_session *sess;
|
||||
|
||||
CALL(make_session(&sess, serve_redir, "http://localhost:7777/foo/bar"));
|
||||
|
||||
ne_redirect_register(sess);
|
||||
|
||||
ONN("followed looping redirect",
|
||||
any_request(sess, "/foo/bar") != NE_ERROR);
|
||||
|
||||
ne_session_destroy(sess);
|
||||
return OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ensure that ne_redirect_location returns NULL when no redirect has
|
||||
* been encountered, or redirect hooks aren't registered. */
|
||||
static int no_redirect(void)
|
||||
{
|
||||
ne_session *sess = ne_session_create("http", "localhost", 7777);
|
||||
const ne_uri *loc;
|
||||
|
||||
ONN("redirect non-NULL before register", ne_redirect_location(sess));
|
||||
ne_redirect_register(sess);
|
||||
ONN("initial redirect non-NULL", ne_redirect_location(sess));
|
||||
|
||||
CALL(spawn_server(7777, single_serve_string,
|
||||
"HTTP/1.0 200 OK\r\n\r\n\r\n"));
|
||||
ONREQ(any_request(sess, "/noredir"));
|
||||
CALL(await_server());
|
||||
|
||||
ONN("redirect non-NULL after non-redir req", ne_redirect_location(sess));
|
||||
|
||||
CALL(spawn_server(7777, single_serve_string, "HTTP/1.0 302 Get Ye Away\r\n"
|
||||
"Location: /blah\r\n" "\r\n"));
|
||||
CALL(process_redir(sess, "/foo", &loc));
|
||||
CALL(await_server());
|
||||
|
||||
ne_session_destroy(sess);
|
||||
return OK;
|
||||
}
|
||||
|
||||
ne_test tests[] = {
|
||||
T(lookup_localhost),
|
||||
T(simple),
|
||||
T(non_absolute),
|
||||
T(relative_1),
|
||||
T(relative_2),
|
||||
T(no_redirect),
|
||||
T(NULL)
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
Test program for the neon resolver interface
|
||||
Copyright (C) 2002-2003, Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "ne_socket.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
ne_sock_addr *addr;
|
||||
char buf[256];
|
||||
int ret = 0;
|
||||
|
||||
if (argc < 2) {
|
||||
printf("Usage: %s hostname\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ne_sock_init()) {
|
||||
printf("%s: Failed to initialize socket library.\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
addr = ne_addr_resolve(argv[1], 0);
|
||||
if (ne_addr_result(addr)) {
|
||||
printf("Could not resolve `%s': %s\n", argv[1],
|
||||
ne_addr_error(addr, buf, sizeof buf));
|
||||
ret = 2;
|
||||
} else {
|
||||
const ne_inet_addr *ia;
|
||||
printf("Resolved `%s' OK:", argv[1]);
|
||||
for (ia = ne_addr_first(addr); ia; ia = ne_addr_next(addr)) {
|
||||
printf(" <%s>", ne_iaddr_print(ia, buf, sizeof buf));
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
ne_addr_destroy(addr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# This script can be used to run the installed neon test suite
|
||||
# against an installed copy of the neon library.
|
||||
#
|
||||
|
||||
# enable glibc malloc safety checks
|
||||
MALLOC_CHECK_=2
|
||||
export MALLOC_CHECK_
|
||||
|
||||
cd data
|
||||
|
||||
if test -x ../bin/ssl; then
|
||||
rm -rf ca
|
||||
|
||||
echo "INIT: generating SSL ceritifcates..."
|
||||
if sh ./makekeys.sh 2> makekeys.out; then :; else
|
||||
echo FAIL: could not generate SSL certificates
|
||||
exit 2
|
||||
fi
|
||||
fi
|
||||
|
||||
for t in ../bin/*; do
|
||||
if ${t}; then :; else
|
||||
echo FAIL: ${t}
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo "PASS: all tests passed"
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
|
||||
rm -f debug.log child.log
|
||||
|
||||
ulimit -c unlimited
|
||||
ulimit -v 10240
|
||||
|
||||
# enable an safety-checking malloc in glibc which will abort() if
|
||||
# heap corruption is detected.
|
||||
MALLOC_CHECK_=2
|
||||
export MALLOC_CHECK_
|
||||
|
||||
for f in $*; do
|
||||
if ${HARNESS} ./$f ${SRCDIR}; then
|
||||
:
|
||||
else
|
||||
echo FAILURE
|
||||
[ -z "$CARRYON" ] && exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,9 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIBOwIBAAJBAPNFTmxnz4JZA+8+SonD0qWgSBPYWrNlH1FP+psm5EGZGmGJGvSD
|
||||
sk6HkyvstdopKF50UuEaJ263IorAhkmdGG0CAwEAAQJAJBhYdoVAqNqEVu8rKB3C
|
||||
F4kcqLUlYBDVAL+ZM4QlwgWncAKk2C53BwH4PVWIIfyysleyt3bTAtqg/tgMNM06
|
||||
AQIhAP1HKbuppa+UY4rNP4Xcyj5BrCU4wVz77sg/ygW+mWIhAiEA9eKcUnnaIpig
|
||||
hlWtx9qz++85/JtahA85j6T48v0hBM0CIQCa8ByUg2wq45CdSX+xiOZjfVMslfKb
|
||||
yjZBY9xW9UjpYQIgdy9j5JqKANEIpnTran95VLot2mMXagHTPeySe331PlUCIQD0
|
||||
rL1AXeIR3Vd4D8dgab/FVbg4i94qBiY0731nyPJRoQ==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
Tests for session handling
|
||||
Copyright (C) 2002-2003, Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "ne_session.h"
|
||||
|
||||
#include "tests.h"
|
||||
|
||||
/* should be in a 'session.c'? */
|
||||
static int fill_uri(void)
|
||||
{
|
||||
ne_uri uri = {0};
|
||||
ne_session *sess = ne_session_create("http", "localhost", 7777);
|
||||
|
||||
ne_fill_server_uri(sess, &uri);
|
||||
|
||||
ONN("hostname mis-match", strcmp(uri.host, "localhost"));
|
||||
ONN("port mis-match", uri.port != 7777);
|
||||
ONN("scheme mis-match", strcmp(uri.scheme, "http"));
|
||||
|
||||
ne_session_destroy(sess);
|
||||
ne_uri_free(&uri);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int match_hostport(const char *scheme, const char *hostname, int port,
|
||||
const char *hostport)
|
||||
{
|
||||
ne_session *sess = ne_session_create(scheme, hostname, port);
|
||||
const char *hp = ne_get_server_hostport(sess);
|
||||
ONV(strcmp(hp, hostport),
|
||||
("hostport incorrect for %s: `%s' not `%s'", scheme, hp, hostport));
|
||||
ne_session_destroy(sess);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int hostports(void)
|
||||
{
|
||||
static const struct {
|
||||
const char *scheme, *hostname;
|
||||
int port;
|
||||
const char *hostport;
|
||||
} hps[] = {
|
||||
{ "http", "host.name", 80, "host.name" },
|
||||
{ "http", "host.name", 555, "host.name:555" },
|
||||
{ "http", "host.name", 443, "host.name:443" },
|
||||
{ "https", "host.name", 80, "host.name:80" },
|
||||
{ "https", "host.name", 443, "host.name" },
|
||||
{ "https", "host.name", 700, "host.name:700" },
|
||||
{ NULL }
|
||||
};
|
||||
int n;
|
||||
|
||||
for (n = 0; hps[n].scheme; n++) {
|
||||
CALL(match_hostport(hps[n].scheme, hps[n].hostname,
|
||||
hps[n].port, hps[n].hostport));
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
||||
/* Check that ne_set_error is passing through to printf correctly. */
|
||||
static int errors(void)
|
||||
{
|
||||
ne_session *sess = ne_session_create("http", "foo.com", 80);
|
||||
|
||||
#define EXPECT "foo, hello world, 100, bar!"
|
||||
|
||||
ne_set_error(sess, "foo, %s, %d, bar!", "hello world", 100);
|
||||
|
||||
ONV(strcmp(ne_get_error(sess), EXPECT),
|
||||
("session error was `%s' not `%s'",
|
||||
ne_get_error(sess), EXPECT));
|
||||
#undef EXPECT
|
||||
|
||||
ne_session_destroy(sess);
|
||||
return OK;
|
||||
}
|
||||
|
||||
#define ID1 "foo"
|
||||
#define ID2 "bar"
|
||||
|
||||
static int privates(void)
|
||||
{
|
||||
ne_session *sess = ne_session_create("http", "localhost", 80);
|
||||
char *v1 = "hello", *v2 = "world";
|
||||
|
||||
ne_set_session_private(sess, ID1, v1);
|
||||
ne_set_session_private(sess, ID2, v2);
|
||||
|
||||
#define PRIV(msg, id, val) \
|
||||
ONN(msg, ne_get_session_private(sess, id) != val)
|
||||
|
||||
PRIV("private #1 wrong", ID1, v1);
|
||||
PRIV("private #2 wrong", ID2, v2);
|
||||
PRIV("unknown id wrong", "no such ID", NULL);
|
||||
|
||||
ne_session_destroy(sess);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* test that ne_session_create doesn't really care what scheme you
|
||||
* give it, and that ne_get_scheme() works. */
|
||||
static int get_scheme(void)
|
||||
{
|
||||
static const char *schemes[] = {
|
||||
"http", "https", "ftp", "ldap", "foobar", NULL
|
||||
};
|
||||
int n;
|
||||
|
||||
for (n = 0; schemes[n]; n++) {
|
||||
ne_session *sess = ne_session_create(schemes[n], "localhost", 80);
|
||||
ONV(strcmp(ne_get_scheme(sess), schemes[n]),
|
||||
("scheme was `%s' not `%s'!", ne_get_scheme(sess), schemes[n]));
|
||||
ne_session_destroy(sess);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
ne_test tests[] = {
|
||||
T(fill_uri),
|
||||
T(hostports),
|
||||
T(errors),
|
||||
T(privates),
|
||||
T(get_scheme),
|
||||
T(NULL)
|
||||
};
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
neon test suite
|
||||
Copyright (C) 2002-2003, Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "ne_request.h"
|
||||
#include "ne_socket.h"
|
||||
|
||||
#include "tests.h"
|
||||
#include "child.h"
|
||||
#include "utils.h"
|
||||
|
||||
static int foo(void)
|
||||
{
|
||||
/* This is a skeleton test suite file. */
|
||||
return OK;
|
||||
}
|
||||
|
||||
ne_test tests[] = {
|
||||
T(foo), /* test functions here */
|
||||
|
||||
/* end of test functions. */
|
||||
T(NULL)
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,532 @@
|
||||
/*
|
||||
String handling tests
|
||||
Copyright (C) 2001-2005, Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h> /* for the ENOENT definitions in str_errors */
|
||||
#endif
|
||||
|
||||
#include "ne_string.h"
|
||||
|
||||
#include "tests.h"
|
||||
|
||||
#undef ONCMP
|
||||
#define ONCMP(a,b) ONV(strcmp(a, b), \
|
||||
("result was [%s] not [%s]", a, b))
|
||||
|
||||
static int simple(void) {
|
||||
ne_buffer *s = ne_buffer_create();
|
||||
ON(s == NULL);
|
||||
ne_buffer_zappend(s, "abcde");
|
||||
ONCMP(s->data, "abcde");
|
||||
ON(ne_buffer_size(s) != 5);
|
||||
ne_buffer_destroy(s);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int buf_concat(void)
|
||||
{
|
||||
ne_buffer *s = ne_buffer_create();
|
||||
ON(s == NULL);
|
||||
ne_buffer_concat(s, "a", "b", "c", "d", "e", "f", "g", NULL);
|
||||
ONCMP(s->data, "abcdefg");
|
||||
ON(ne_buffer_size(s) != 7);
|
||||
ne_buffer_destroy(s);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int buf_concat2(void)
|
||||
{
|
||||
#define RES "alphabetagammadeltaepsilonetatheta"
|
||||
ne_buffer *s = ne_buffer_create();
|
||||
ON(s == NULL);
|
||||
ne_buffer_concat(s, "alpha", "beta", "gamma", "delta", "epsilon",
|
||||
"eta", "theta", NULL);
|
||||
ONCMP(s->data, RES);
|
||||
ON(ne_buffer_size(s) != strlen(RES));
|
||||
ne_buffer_destroy(s);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int buf_concat3(void)
|
||||
{
|
||||
ne_buffer *s = ne_buffer_create();
|
||||
ON(s == NULL);
|
||||
ne_buffer_zappend(s, "foobar");
|
||||
ne_buffer_concat(s, "norman", NULL);
|
||||
ONCMP(s->data, "foobarnorman");
|
||||
ON(ne_buffer_size(s) != 12);
|
||||
ne_buffer_destroy(s);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int append(void)
|
||||
{
|
||||
ne_buffer *s = ne_buffer_create();
|
||||
ON(s == NULL);
|
||||
ne_buffer_append(s, "a", 1);
|
||||
ne_buffer_append(s, "b", 1);
|
||||
ne_buffer_append(s, "c", 1);
|
||||
ONCMP(s->data, "abc");
|
||||
ON(ne_buffer_size(s) != 3);
|
||||
ne_buffer_zappend(s, "hello");
|
||||
ONCMP(s->data, "abchello");
|
||||
ne_buffer_czappend(s, "world");
|
||||
ONCMP(s->data, "abchelloworld");
|
||||
ON(ne_buffer_size(s) != 13);
|
||||
ne_buffer_destroy(s);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int grow(void)
|
||||
{
|
||||
ne_buffer *s = ne_buffer_ncreate(2);
|
||||
ON(s == NULL);
|
||||
ne_buffer_append(s, "a", 1);
|
||||
ne_buffer_grow(s, 4);
|
||||
ONCMP(s->data, "a");
|
||||
ne_buffer_destroy(s);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int alter(void) {
|
||||
ne_buffer *s = ne_buffer_create();
|
||||
char *d;
|
||||
ON(s == NULL);
|
||||
ne_buffer_zappend(s, "abcdefg");
|
||||
d = s->data;
|
||||
ON(d == NULL);
|
||||
d[2] = '\0';
|
||||
ne_buffer_altered(s);
|
||||
ONCMP(s->data, "ab");
|
||||
ON(ne_buffer_size(s) != 2);
|
||||
ne_buffer_zappend(s, "hijkl");
|
||||
ONCMP(s->data, "abhijkl");
|
||||
ne_buffer_destroy(s);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* Macros for testing ne_token. */
|
||||
|
||||
#define TEST(res) do { \
|
||||
char *tok = ne_token(&pnt, ','); \
|
||||
ONN(res ": return", tok == NULL); \
|
||||
ONN(res ": compare", strcmp(tok, (res))); \
|
||||
ONN(res ": modify", pnt == NULL); \
|
||||
} while (0)
|
||||
|
||||
#define LASTTEST(res) do { \
|
||||
char *tok = ne_token(&pnt, ','); \
|
||||
ONN(res ": last return", tok == NULL); \
|
||||
ONN(res ": last compare", strcmp(tok, (res))); \
|
||||
ONN(res ": last modify", pnt != NULL); \
|
||||
} while (0)
|
||||
|
||||
#define QTEST(res) do { \
|
||||
char *tok = ne_qtoken(&pnt, ',', QUOTES); \
|
||||
ONN(res ": return", tok == NULL); \
|
||||
ONN(res ": compare", strcmp(tok, (res))); \
|
||||
ONN(res ": modify", pnt == NULL); \
|
||||
} while (0)
|
||||
|
||||
#define QLASTTEST(res) do { \
|
||||
char *tok = ne_qtoken(&pnt, ',', QUOTES); \
|
||||
ONN(res ": last return", tok == NULL); \
|
||||
ONN(res ": last compare", strcmp(tok, (res))); \
|
||||
ONN(res ": last modify", pnt != NULL); \
|
||||
} while (0)
|
||||
|
||||
static int token1(void)
|
||||
{
|
||||
char *str = ne_strdup("a,b,c,d"), *pnt = str;
|
||||
|
||||
TEST("a"); TEST("b"); TEST("c"); LASTTEST("d");
|
||||
|
||||
ne_free(str);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int token2(void)
|
||||
{
|
||||
char *str = ne_strdup("norman,fishing, elsewhere"), *pnt = str;
|
||||
|
||||
TEST("norman"); TEST("fishing"); LASTTEST(" elsewhere");
|
||||
|
||||
ne_free(str);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int nulls(void)
|
||||
{
|
||||
char *str = ne_strdup("alpha,,gamma"), *pnt = str;
|
||||
|
||||
TEST("alpha"); TEST(""); LASTTEST("gamma");
|
||||
ne_free(str);
|
||||
|
||||
pnt = str = ne_strdup(",,,wooo");
|
||||
TEST(""); TEST(""); TEST(""); LASTTEST("wooo");
|
||||
ne_free(str);
|
||||
|
||||
pnt = str = ne_strdup("wooo,,,");
|
||||
TEST("wooo"); TEST(""); TEST(""); LASTTEST("");
|
||||
ne_free(str);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int empty(void)
|
||||
{
|
||||
char *str = ne_strdup(""), *pnt = str;
|
||||
|
||||
LASTTEST("");
|
||||
ne_free(str);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
#undef QUOTES
|
||||
#define QUOTES "'"
|
||||
|
||||
static int quoted(void)
|
||||
{
|
||||
char *str =
|
||||
ne_strdup("alpha,'beta, a fish called HELLO!?',sandwiches");
|
||||
char *pnt = str;
|
||||
|
||||
QTEST("alpha");
|
||||
QTEST("'beta, a fish called HELLO!?'");
|
||||
QLASTTEST("sandwiches");
|
||||
|
||||
ne_free(str);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int badquotes(void)
|
||||
{
|
||||
char *str = ne_strdup("alpha,'blah"), *pnt = str;
|
||||
|
||||
QTEST("alpha");
|
||||
ON(ne_qtoken(&pnt, ',', QUOTES) != NULL);
|
||||
|
||||
ne_free(str);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* for testing ne_shave. */
|
||||
#undef TEST
|
||||
#define TEST(str, ws, res) do { \
|
||||
char *s = ne_strdup((str)); \
|
||||
char *r = ne_shave(s, (ws)); \
|
||||
ONN("[" str "]", strcmp(r, (res))); \
|
||||
ne_free(s); \
|
||||
} while (0)
|
||||
|
||||
static int shave(void)
|
||||
{
|
||||
TEST(" b ", " ", "b");
|
||||
TEST("b", " ", "b");
|
||||
TEST(" b ", " ", "b");
|
||||
TEST("--bbb-----", "-", "bbb");
|
||||
TEST("hello, world ", " ", "hello, world");
|
||||
TEST("<<><<<><<this is foo<<><<<<><<", "<>", "this is foo");
|
||||
TEST("09809812342347I once saw an helicopter0012312312398", "0123456789",
|
||||
"I once saw an helicopter");
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* Regression test for ne_shave call which should produce an empty
|
||||
* string. */
|
||||
static int shave_regress(void)
|
||||
{
|
||||
TEST("\"\"", "\"", "");
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* Test the ne_token/ne_shave combination. */
|
||||
|
||||
#undef TEST
|
||||
#undef LASTTEST
|
||||
|
||||
#define TEST(res) do { \
|
||||
char *tok = ne_token(&pnt, ','); \
|
||||
ONN(res ": return", tok == NULL); \
|
||||
tok = ne_shave(tok, " "); \
|
||||
ONN(res ": shave", tok == NULL); \
|
||||
ONN(res ": compare", strcmp(tok, (res))); \
|
||||
ONN(res ": modify", pnt == NULL); \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define LASTTEST(res) do { \
|
||||
char *tok = ne_token(&pnt, ','); \
|
||||
ONN(res ": last return", tok == NULL); \
|
||||
tok = ne_shave(tok, " "); \
|
||||
ONN(res ": last shave", tok == NULL); \
|
||||
ONN(res ": last compare", strcmp(tok, (res))); \
|
||||
ONN(res ": last modify", pnt != NULL); \
|
||||
} while (0)
|
||||
|
||||
/* traditional use of ne_token/ne_shave. */
|
||||
static int combo(void)
|
||||
{
|
||||
char *str = ne_strdup(" fred , mary, jim , alice, david"), *pnt = str;
|
||||
|
||||
TEST("fred"); TEST("mary"); TEST("jim"); TEST("alice");
|
||||
LASTTEST("david");
|
||||
|
||||
ne_free(str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int concat(void)
|
||||
{
|
||||
#define CAT(res, args) do { char *str = ne_concat args; \
|
||||
ONCMP(str, res); \
|
||||
ne_free(str); } while (0)
|
||||
CAT("alphabeta", ("alpha", "beta", NULL));
|
||||
CAT("alpha", ("alpha", "", "", NULL));
|
||||
CAT("", ("", NULL));
|
||||
CAT("", ("", "", "", NULL));
|
||||
CAT("alpha", ("", "a", "lph", "", "a", NULL));
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int str_errors(void)
|
||||
{
|
||||
char expect[200], actual[200];
|
||||
|
||||
strncpy(expect, strerror(ENOENT), sizeof(expect));
|
||||
ONN("ne_strerror did not return passed-in buffer",
|
||||
ne_strerror(ENOENT, actual, sizeof(actual)) != actual);
|
||||
|
||||
ONV(strcmp(expect, actual),
|
||||
("error from ENOENT was `%s' not `%s'", actual, expect));
|
||||
|
||||
/* Test truncated error string is still NUL-terminated. */
|
||||
ne_strerror(ENOENT, actual, 6);
|
||||
ONN("truncated string had wrong length", strlen(actual) != 5);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int strnzcpy(void)
|
||||
{
|
||||
char buf[5];
|
||||
|
||||
ne_strnzcpy(buf, "abcdefghi", sizeof buf);
|
||||
ONV(strcmp(buf, "abcd"), ("result was `%s' not `abcd'", buf));
|
||||
|
||||
ne_strnzcpy(buf, "ab", sizeof buf);
|
||||
ONV(strcmp(buf, "ab"), ("result was `%s' not `ab'", buf));
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
#define FOX_STRING "The quick brown fox jumped over the lazy dog"
|
||||
#define PUNC_STRING "<>,.;'#:@~[]{}!\"$%^&*()_+-="
|
||||
|
||||
static int cleaner(void)
|
||||
{
|
||||
static const char *strings[] = {
|
||||
"alpha", "alpha",
|
||||
"pretty\033[41mcolours", "pretty [41mcolours",
|
||||
"beta\n", "beta ",
|
||||
"del\rt\na", "del t a",
|
||||
FOX_STRING, FOX_STRING,
|
||||
"0123456789", "0123456789",
|
||||
PUNC_STRING, PUNC_STRING,
|
||||
"\01blah blee\05bloo", " blah blee bloo",
|
||||
NULL,
|
||||
};
|
||||
unsigned int n;
|
||||
|
||||
for (n = 0; strings[n]; n+=2) {
|
||||
char *act = ne_strclean(ne_strdup(strings[n]));
|
||||
|
||||
ONV(strcmp(act, strings[n+1]),
|
||||
("cleansed to `%s' not `%s'", act, strings[n+1]));
|
||||
|
||||
ne_free(act);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* Check that raw data 'raw', of length 'len', has base64 encoding
|
||||
* of 'expected'. */
|
||||
static int b64_check(const unsigned char *raw, size_t len,
|
||||
const char *expected)
|
||||
{
|
||||
char *encoded = ne_base64(raw, len);
|
||||
unsigned char *decoded;
|
||||
size_t dlen;
|
||||
|
||||
ONV(strcmp(encoded, expected),
|
||||
("base64(\"%s\") gave \"%s\" not \"%s\"", raw, encoded, expected));
|
||||
|
||||
dlen = ne_unbase64(encoded, &decoded);
|
||||
ONV(dlen != len,
|
||||
("decoded `%s' length was %" NE_FMT_SIZE_T " not %" NE_FMT_SIZE_T,
|
||||
expected, dlen, len));
|
||||
|
||||
ONV(memcmp(raw, decoded, dlen),
|
||||
("decoded `%s' as `%.*s' not `%.*s'",
|
||||
expected, (int)dlen, decoded, (int)dlen, raw));
|
||||
|
||||
ne_free(decoded);
|
||||
ne_free(encoded);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* ALLBITS: base64 encoding of "\0..\377" */
|
||||
#define ALLBITS \
|
||||
"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKiss" \
|
||||
"LS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZ" \
|
||||
"WltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWG" \
|
||||
"h4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKz" \
|
||||
"tLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g" \
|
||||
"4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w=="
|
||||
|
||||
static int base64(void)
|
||||
{
|
||||
unsigned char bits[256];
|
||||
size_t n;
|
||||
|
||||
#define B64B(x, l, y) CALL(b64_check((unsigned char *)x, l, y))
|
||||
#define B64(x, y) B64B(x, strlen(x), y)
|
||||
|
||||
/* invent these with
|
||||
* $ printf "string" | uuencode -m blah
|
||||
*/
|
||||
B64("a", "YQ==");
|
||||
B64("bb", "YmI=");
|
||||
B64("ccc", "Y2Nj");
|
||||
B64("Hello, world", "SGVsbG8sIHdvcmxk");
|
||||
B64("Aladdin:open sesame", "QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
|
||||
B64("I once saw a dog called norman.\n",
|
||||
"SSBvbmNlIHNhdyBhIGRvZyBjYWxsZWQgbm9ybWFuLgo=");
|
||||
B64("The quick brown fox jumped over the lazy dog",
|
||||
"VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZWQgb3ZlciB0aGUgbGF6eSBkb2c=");
|
||||
|
||||
/* binary data..
|
||||
* $ printf "string" | wc -c # get the length
|
||||
* $ printf "string" | uuencode -m blah # get the base64
|
||||
*/
|
||||
B64B("\0\0\0\0\0\n", 6, "AAAAAAAK");
|
||||
B64B("I once wished \0 upon a \0 fish.", 30,
|
||||
"SSBvbmNlIHdpc2hlZCAAIHVwb24gYSAAIGZpc2gu");
|
||||
B64B("\201\202\203\204", 4, "gYKDhA==");
|
||||
|
||||
for (n = 0; n < sizeof bits; n++)
|
||||
bits[n] = (unsigned char)n;
|
||||
CALL(b64_check(bits, sizeof bits, ALLBITS));
|
||||
|
||||
#undef B64
|
||||
#undef B64B
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int unbase64(void)
|
||||
{
|
||||
static const char *ts[] = {
|
||||
"", "a", "ab", "abc",
|
||||
"}bcd", "a}cd", "ab}d", "abc}", " ",
|
||||
"^bcd", "a^cd", "ab^d", "abc^",
|
||||
"====", "=bcd", "a=cd", "ab=d", "a==d", "a=c=",
|
||||
NULL
|
||||
};
|
||||
size_t n;
|
||||
|
||||
for (n = 0; ts[n]; n++) {
|
||||
unsigned char *tmp;
|
||||
ONV(ne_unbase64(ts[n], &tmp) != 0,
|
||||
("invalid string `%s' was decoded", ts[n]));
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int printing(void)
|
||||
{
|
||||
struct {
|
||||
const char *in, *out;
|
||||
size_t pass, ret;
|
||||
} ts[] = {
|
||||
{ "alpha", "alpha", 10, 5 },
|
||||
{ "alpha", "alph", 5, 4 },
|
||||
{ "foobar", "", 1, 0 },
|
||||
{ NULL, NULL, 0, 0}
|
||||
};
|
||||
size_t n;
|
||||
|
||||
for (n = 0; ts[n].in; n++) {
|
||||
char buf[512];
|
||||
size_t ret;
|
||||
|
||||
memset(buf, 'A', sizeof buf);
|
||||
|
||||
ret = ne_snprintf(buf, ts[n].pass, "%s", ts[n].in);
|
||||
|
||||
ONCMP(buf, ts[n].out);
|
||||
ONV(ret != ts[n].ret,
|
||||
("got return value %" NE_FMT_SIZE_T " not %" NE_FMT_SIZE_T,
|
||||
ret, ts[n].ret));
|
||||
|
||||
/* byte past the NUL must still be 'A' */
|
||||
ONN("buffer over-ran!", buf[ret + 1] != 'A');
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
ne_test tests[] = {
|
||||
T(simple),
|
||||
T(buf_concat),
|
||||
T(buf_concat2),
|
||||
T(buf_concat3),
|
||||
T(append),
|
||||
T(grow),
|
||||
T(alter),
|
||||
T(token1),
|
||||
T(token2),
|
||||
T(nulls),
|
||||
T(empty),
|
||||
T(quoted),
|
||||
T(badquotes),
|
||||
T(shave),
|
||||
T(shave_regress),
|
||||
T(combo),
|
||||
T(concat),
|
||||
T(str_errors),
|
||||
T(strnzcpy),
|
||||
T(cleaner),
|
||||
T(base64),
|
||||
T(unbase64),
|
||||
T(printing),
|
||||
T(NULL)
|
||||
};
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
neon test suite
|
||||
Copyright (C) 2002-2005, Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
/** These tests show that the stub functions produce appropriate
|
||||
* results to provide ABI-compatibility when a particular feature is
|
||||
* not supported by the library.
|
||||
**/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "ne_request.h"
|
||||
#include "ne_socket.h"
|
||||
#include "ne_compress.h"
|
||||
|
||||
#include "tests.h"
|
||||
#include "child.h"
|
||||
#include "utils.h"
|
||||
|
||||
#if defined(NE_HAVE_ZLIB) && defined(NE_HAVE_SSL)
|
||||
#define NO_TESTS 1
|
||||
#endif
|
||||
|
||||
#define EOL "\r\n"
|
||||
|
||||
#ifndef NE_HAVE_ZLIB
|
||||
static int sd_result = OK;
|
||||
|
||||
static int sd_reader(void *ud, const char *block, size_t len)
|
||||
{
|
||||
const char *expect = ud;
|
||||
if (strncmp(expect, block, len) != 0) {
|
||||
sd_result = FAIL;
|
||||
t_context("decompress reader got bad data");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stub_decompress(void)
|
||||
{
|
||||
ne_session *sess;
|
||||
ne_decompress *dc;
|
||||
ne_request *req;
|
||||
int ret;
|
||||
|
||||
CALL(make_session(&sess, single_serve_string,
|
||||
"HTTP/1.1 200 OK" EOL
|
||||
"Connection: close" EOL EOL
|
||||
"abcde"));
|
||||
|
||||
req = ne_request_create(sess, "GET", "/foo");
|
||||
|
||||
dc = ne_decompress_reader(req, ne_accept_2xx, sd_reader, "abcde");
|
||||
|
||||
ret = ne_request_dispatch(req);
|
||||
|
||||
CALL(await_server());
|
||||
|
||||
ONREQ(ret);
|
||||
|
||||
ne_decompress_destroy(dc);
|
||||
ne_request_destroy(req);
|
||||
ne_session_destroy(sess);
|
||||
|
||||
/* This is a skeleton test suite file. */
|
||||
return sd_result;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef NE_HAVE_SSL
|
||||
static int stub_ssl(void)
|
||||
{
|
||||
ne_session *sess = ne_session_create("https", "localhost", 7777);
|
||||
ne_ssl_certificate *cert;
|
||||
ne_ssl_client_cert *cc;
|
||||
|
||||
/* these should all fail when SSL is not supported. */
|
||||
cert = ne_ssl_cert_read("Makefile");
|
||||
if (cert) {
|
||||
char *dn, digest[60], date[NE_SSL_VDATELEN];
|
||||
const ne_ssl_certificate *issuer;
|
||||
|
||||
/* This branch should never be executed, but lets pretend it
|
||||
* will to prevent the compiler optimising this code away if
|
||||
* it's placed after the cert != NULL test. And all that
|
||||
* needs to be tested is that these functions link OK. */
|
||||
dn = ne_ssl_readable_dname(ne_ssl_cert_subject(cert));
|
||||
ONN("this code shouldn't run", dn != NULL);
|
||||
dn = ne_ssl_readable_dname(ne_ssl_cert_issuer(cert));
|
||||
ONN("this code shouldn't run", dn != NULL);
|
||||
issuer = ne_ssl_cert_signedby(cert);
|
||||
ONN("this code shouldn't run", issuer != NULL);
|
||||
ONN("this code shouldn't run", ne_ssl_cert_digest(cert, digest));
|
||||
ne_ssl_cert_validity(cert, date, date);
|
||||
ONN("this code shouldn't run",
|
||||
ne_ssl_dname_cmp(ne_ssl_cert_subject(cert),
|
||||
ne_ssl_cert_issuer(cert)));
|
||||
ONN("this code shouldn't run", ne_ssl_cert_identity(issuer) != NULL);
|
||||
ONN("this code shouldn't run", ne_ssl_cert_export(cert) != NULL);
|
||||
}
|
||||
|
||||
ONN("this code shouldn't run", ne_ssl_cert_import("foo") != NULL);
|
||||
ONN("this code shouldn't run", ne_ssl_cert_read("Makefile") != NULL);
|
||||
ONN("this code shouldn't succeed", ne_ssl_cert_cmp(NULL, NULL) == 0);
|
||||
|
||||
ONN("certificate load succeeded", cert != NULL);
|
||||
ne_ssl_cert_free(cert);
|
||||
|
||||
cc = ne_ssl_clicert_read("Makefile");
|
||||
if (cc) {
|
||||
const char *name;
|
||||
/* dead branch as above. */
|
||||
cert = (void *)ne_ssl_clicert_owner(cc);
|
||||
ONN("this code shouldn't run", cert != NULL);
|
||||
name = ne_ssl_clicert_name(cc);
|
||||
ONN("this code shouldn't run", name != NULL);
|
||||
ONN("this code shouldn't run", ne_ssl_clicert_decrypt(cc, "fubar"));
|
||||
ne_ssl_set_clicert(sess, cc);
|
||||
}
|
||||
|
||||
ONN("client certificate load succeeded", cc != NULL);
|
||||
ne_ssl_clicert_free(cc);
|
||||
|
||||
ne_ssl_trust_default_ca(sess);
|
||||
|
||||
ne_session_destroy(sess);
|
||||
return OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef NO_TESTS
|
||||
static int null_test(void) { return OK; }
|
||||
#endif
|
||||
|
||||
ne_test tests[] = {
|
||||
#ifndef NE_HAVE_ZLIB
|
||||
T(stub_decompress),
|
||||
#endif
|
||||
#ifndef NE_HAVE_SSL
|
||||
T(stub_ssl),
|
||||
#endif
|
||||
/* to prevent failure when SSL and zlib are supported. */
|
||||
#ifdef NO_TESTS
|
||||
T(null_test),
|
||||
#endif
|
||||
T(NULL)
|
||||
};
|
||||
|
||||
@@ -0,0 +1,377 @@
|
||||
/*
|
||||
URI handling tests
|
||||
Copyright (C) 2001-2005, Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "ne_uri.h"
|
||||
#include "ne_alloc.h"
|
||||
|
||||
#include "tests.h"
|
||||
|
||||
static int simple(void)
|
||||
{
|
||||
ne_uri p = {0};
|
||||
ON(ne_uri_parse("http://www.webdav.org/foo", &p));
|
||||
ON(strcmp(p.host, "www.webdav.org"));
|
||||
ON(strcmp(p.path, "/foo"));
|
||||
ON(strcmp(p.scheme, "http"));
|
||||
ON(p.port);
|
||||
ON(p.authinfo != NULL);
|
||||
ne_uri_free(&p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int simple_ssl(void)
|
||||
{
|
||||
ne_uri p = {0};
|
||||
ON(ne_uri_parse("https://webdav.org/", &p));
|
||||
ON(strcmp(p.scheme, "https"));
|
||||
ON(p.port);
|
||||
ne_uri_free(&p);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int no_path(void)
|
||||
{
|
||||
ne_uri p = {0};
|
||||
ON(ne_uri_parse("https://webdav.org", &p));
|
||||
ON(strcmp(p.path, "/"));
|
||||
ne_uri_free(&p);
|
||||
return OK;
|
||||
}
|
||||
|
||||
#define STR "/a¹²³¼½/"
|
||||
static int escapes(void)
|
||||
{
|
||||
char *un, *esc;
|
||||
esc = ne_path_escape(STR);
|
||||
ON(esc == NULL);
|
||||
un = ne_path_unescape(esc);
|
||||
ON(un == NULL);
|
||||
ON(strcmp(un, STR));
|
||||
ne_free(un);
|
||||
ne_free(esc);
|
||||
ONN("unescape accepted invalid URI",
|
||||
ne_path_unescape("/foo%zzbar") != NULL);
|
||||
/* no-escape path */
|
||||
esc = ne_path_escape("/foobar");
|
||||
ON(strcmp(esc, "/foobar"));
|
||||
ne_free(esc);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int parents(void)
|
||||
{
|
||||
static const struct {
|
||||
const char *path, *parent;
|
||||
} ps[] = {
|
||||
{ "/a/b/c", "/a/b/" },
|
||||
{ "/a/b/c/", "/a/b/" },
|
||||
{ "/alpha/beta", "/alpha/" },
|
||||
{ "/foo", "/" },
|
||||
{ "norman", NULL },
|
||||
{ "/", NULL },
|
||||
{ "", NULL },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
int n;
|
||||
|
||||
for (n = 0; ps[n].path != NULL; n++) {
|
||||
char *p = ne_path_parent(ps[n].path);
|
||||
if (ps[n].parent == NULL) {
|
||||
ONV(p != NULL, ("parent of `%s' was `%s' not NULL",
|
||||
ps[n].path, p));
|
||||
} else {
|
||||
ONV(p == NULL, ("parent of `%s' was NULL", ps[n].path));
|
||||
ONV(strcmp(p, ps[n].parent),
|
||||
("parent of `%s' was `%s' not `%s'",
|
||||
ps[n].path, p, ps[n].parent));
|
||||
ne_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int compares(void)
|
||||
{
|
||||
const char *alpha = "/alpha";
|
||||
|
||||
ON(ne_path_compare("/a", "/a/") != 0);
|
||||
ON(ne_path_compare("/a/", "/a") != 0);
|
||||
ON(ne_path_compare("/ab", "/a/") == 0);
|
||||
ON(ne_path_compare("/a/", "/ab") == 0);
|
||||
ON(ne_path_compare("/a/", "/a/") != 0);
|
||||
ON(ne_path_compare("/alpha/", "/beta/") == 0);
|
||||
ON(ne_path_compare("/alpha", "/b") == 0);
|
||||
ON(ne_path_compare("/alpha/", "/alphash") == 0);
|
||||
ON(ne_path_compare("/fish/", "/food") == 0);
|
||||
ON(ne_path_compare(alpha, alpha) != 0);
|
||||
ON(ne_path_compare("/a/b/c/d", "/a/b/c/") == 0);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* Checks that a URI comparison of 'u1' and 'u2', which have differing
|
||||
* 'field', doesn't compare to equal; and that they are ordered
|
||||
* correctly. */
|
||||
static int cmp_differ(const char *field,
|
||||
const ne_uri *u1, const ne_uri *u2)
|
||||
{
|
||||
ONV(ne_uri_cmp(u1, u2) == 0,
|
||||
("URIs with different %s were equal", field));
|
||||
|
||||
ONV(ne_uri_cmp(u2, u1) == 0,
|
||||
("URIs with different %s were equal (reversed)", field));
|
||||
|
||||
/* relies on strcmp return value being of equal magnitude when
|
||||
* arguments are reversed; not sure if this is portable
|
||||
* assumption. */
|
||||
ONV(ne_uri_cmp(u1, u2) + ne_uri_cmp(u2, u1) != 0,
|
||||
("relative ordering of URIs with different %s incorrect", field));
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int cmp(void)
|
||||
{
|
||||
ne_uri alpha, beta;
|
||||
|
||||
alpha.path = "/alpha";
|
||||
alpha.scheme = "http";
|
||||
alpha.host = "example.com";
|
||||
alpha.port = 80;
|
||||
|
||||
beta = alpha; /* structure copy. */
|
||||
|
||||
ONN("equal URIs not equal", ne_uri_cmp(&alpha, &beta) != 0);
|
||||
|
||||
beta.path = "/beta";
|
||||
CALL(cmp_differ("path", &alpha, &beta));
|
||||
|
||||
beta = alpha; beta.scheme = "https";
|
||||
CALL(cmp_differ("scheme", &alpha, &beta));
|
||||
|
||||
beta = alpha; beta.port = 433;
|
||||
CALL(cmp_differ("port", &alpha, &beta));
|
||||
|
||||
beta = alpha; beta.host = "fish.com";
|
||||
CALL(cmp_differ("host", &alpha, &beta));
|
||||
|
||||
beta = alpha; beta.host = "EXAMPLE.CoM";
|
||||
ONN("hostname comparison not case-insensitive",
|
||||
ne_uri_cmp(&alpha, &beta) != 0);
|
||||
|
||||
beta = alpha; beta.scheme = "HtTp";
|
||||
ONN("scheme comparison not case-insensitive",
|
||||
ne_uri_cmp(&alpha, &beta) != 0);
|
||||
|
||||
beta = alpha; beta.path = ""; alpha.path = "/";
|
||||
ONN("empty abspath doesn't match '/'",
|
||||
ne_uri_cmp(&alpha, &beta) != 0);
|
||||
ONN("'/' doesn't match empty abspath",
|
||||
ne_uri_cmp(&beta, &alpha) != 0);
|
||||
|
||||
beta = alpha; alpha.path = ""; beta.path = "/foo";
|
||||
ONN("empty abspath matched '/foo'", ne_uri_cmp(&alpha, &beta) == 0);
|
||||
ONN("'/foo' matched empty abspath ", ne_uri_cmp(&beta, &alpha) == 0);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int children(void)
|
||||
{
|
||||
ON(ne_path_childof("/a", "/a/b") == 0);
|
||||
ON(ne_path_childof("/a/", "/a/b") == 0);
|
||||
ON(ne_path_childof("/aa/b/c", "/a/b/c/d/e") != 0);
|
||||
ON(ne_path_childof("////", "/a") != 0);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int slash(void)
|
||||
{
|
||||
ON(ne_path_has_trailing_slash("/a/") == 0);
|
||||
ON(ne_path_has_trailing_slash("/a") != 0);
|
||||
{
|
||||
/* check the uri == "" case. */
|
||||
char *foo = "/";
|
||||
ON(ne_path_has_trailing_slash(&foo[1]));
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int just_hostname(void)
|
||||
{
|
||||
ne_uri p = {0};
|
||||
ON(ne_uri_parse("host.name.com", &p));
|
||||
ON(strcmp(p.host, "host.name.com"));
|
||||
ne_uri_free(&p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int just_path(void)
|
||||
{
|
||||
ne_uri p = {0};
|
||||
ON(ne_uri_parse("/argh", &p));
|
||||
ON(strcmp(p.path, "/argh"));
|
||||
ne_uri_free(&p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int default_port(void)
|
||||
{
|
||||
ONN("default http: port incorrect", ne_uri_defaultport("http") != 80);
|
||||
ONN("default https: port incorrect", ne_uri_defaultport("https") != 443);
|
||||
ONN("unspecified scheme: port incorrect", ne_uri_defaultport("ldap") != 0);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int parse(void)
|
||||
{
|
||||
static const struct test_uri {
|
||||
const char *uri, *scheme, *host;
|
||||
unsigned int port;
|
||||
const char *path, *authinfo;
|
||||
} uritests[] = {
|
||||
{ "http://webdav.org/norman", "http", "webdav.org", 0, "/norman", NULL },
|
||||
{ "http://webdav.org:/norman", "http", "webdav.org", 0, "/norman", NULL },
|
||||
{ "https://webdav.org/foo", "https", "webdav.org", 0, "/foo", NULL },
|
||||
{ "http://webdav.org:8080/bar", "http", "webdav.org", 8080, "/bar", NULL },
|
||||
{ "http://a/b", "http", "a", 0, "/b", NULL },
|
||||
{ "http://webdav.org/bar:fish", "http", "webdav.org", 0, "/bar:fish", NULL },
|
||||
{ "http://webdav.org", "http", "webdav.org", 0, "/", NULL },
|
||||
{ "http://webdav.org/fish@food", "http", "webdav.org", 0, "/fish@food", NULL },
|
||||
/* authinfo */
|
||||
{ "ftp://jim:bob@jim.com", "ftp", "jim.com", 0, "/", "jim:bob" },
|
||||
{ "ldap://fred:bloggs@fish.com/foobar", "ldap", "fish.com", 0,
|
||||
"/foobar", "fred:bloggs" },
|
||||
/* relativeURIs accepted for dubious legacy reasons. */
|
||||
{ "a/b", NULL, "a", 0, "/b", NULL },
|
||||
{ "a:8080/b", NULL, "a", 8080, "/b", NULL },
|
||||
{ "/fish", NULL, NULL, 0, "/fish", NULL },
|
||||
{ "webdav.org:8080", NULL, "webdav.org", 8080, "/", NULL },
|
||||
/* IPv6 hex strings allowed even if IPv6 not supported. */
|
||||
{ "http://[::1]/foo", "http", "[::1]", 0, "/foo", NULL },
|
||||
{ "http://[a:a:a:a::0]/foo", "http", "[a:a:a:a::0]", 0, "/foo", NULL },
|
||||
{ "http://[::1]:8080/bar", "http", "[::1]", 8080, "/bar", NULL },
|
||||
{ "ftp://[feed::cafe]:555", "ftp", "[feed::cafe]", 555, "/", NULL },
|
||||
{ "http://fish/[foo]/bar", "http", "fish", 0, "/[foo]/bar", NULL },
|
||||
/* and some dubious ones: */
|
||||
{ "[::1]/foo", NULL, "[::1]", 0, "/foo", NULL },
|
||||
{ "[::1]:8000/foo", NULL, "[::1]", 8000, "/foo", NULL },
|
||||
{ NULL }
|
||||
};
|
||||
int n;
|
||||
|
||||
for (n = 0; uritests[n].uri != NULL; n++) {
|
||||
ne_uri res;
|
||||
const struct test_uri *e = &uritests[n];
|
||||
ONV(ne_uri_parse(e->uri, &res) != 0,
|
||||
("%s: parse failed", e->uri));
|
||||
ONV(res.port != e->port,
|
||||
("%s: parsed port was %d not %d", e->uri, res.port, e->port));
|
||||
ONCMP(e->scheme, res.scheme, e->uri, "scheme");
|
||||
ONCMP(e->host, res.host, e->uri, "host");
|
||||
ONV(strcmp(res.path, e->path),
|
||||
("%s: parsed path was %s not %s", e->uri, res.path, e->path));
|
||||
ONCMP(e->authinfo, res.authinfo, e->uri, "authinfo");
|
||||
ne_uri_free(&res);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int failparse(void)
|
||||
{
|
||||
static const char *uris[] = {
|
||||
"",
|
||||
"http://[::1/",
|
||||
NULL
|
||||
};
|
||||
int n;
|
||||
|
||||
for (n = 0; uris[n] != NULL; n++) {
|
||||
ne_uri p;
|
||||
ONV(ne_uri_parse(uris[n], &p) == 0,
|
||||
("`%s' did not fail to parse", uris[n]));
|
||||
ne_uri_free(&p);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unparse(void)
|
||||
{
|
||||
const char *uris[] = {
|
||||
"http://foo.com/bar",
|
||||
"https://bar.com/foo/wishbone",
|
||||
"http://www.random.com:8000/",
|
||||
"http://[::1]:8080/",
|
||||
"ftp://ftp.foo.bar/abc/def",
|
||||
NULL
|
||||
};
|
||||
int n;
|
||||
|
||||
for (n = 0; uris[n] != NULL; n++) {
|
||||
ne_uri parsed;
|
||||
char *unp;
|
||||
|
||||
ONV(ne_uri_parse(uris[n], &parsed),
|
||||
("failed to parse %s", uris[n]));
|
||||
|
||||
if (parsed.port == 0)
|
||||
parsed.port = ne_uri_defaultport(parsed.scheme);
|
||||
|
||||
unp = ne_uri_unparse(&parsed);
|
||||
|
||||
ONV(strcmp(unp, uris[n]),
|
||||
("unparse got %s from %s", unp, uris[n]));
|
||||
|
||||
ne_uri_free(&parsed);
|
||||
ne_free(unp);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
ne_test tests[] = {
|
||||
T(simple),
|
||||
T(simple_ssl),
|
||||
T(no_path),
|
||||
T(escapes),
|
||||
T(parents),
|
||||
T(compares),
|
||||
T(cmp),
|
||||
T(children),
|
||||
T(slash),
|
||||
T(just_hostname),
|
||||
T(just_path),
|
||||
T(default_port),
|
||||
T(parse),
|
||||
T(failparse),
|
||||
T(unparse),
|
||||
T(NULL)
|
||||
};
|
||||
@@ -0,0 +1,294 @@
|
||||
/*
|
||||
utils tests
|
||||
Copyright (C) 2001-2005, Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "ne_utils.h"
|
||||
#include "ne_md5.h"
|
||||
#include "ne_alloc.h"
|
||||
#include "ne_dates.h"
|
||||
#include "ne_string.h"
|
||||
|
||||
#include "tests.h"
|
||||
|
||||
static const struct {
|
||||
const char *status;
|
||||
int major, minor, code;
|
||||
const char *rp;
|
||||
} accept_sl[] = {
|
||||
/* These are really valid. */
|
||||
{ "HTTP/1.1 200 OK", 1, 1, 200, "OK" },
|
||||
{ "HTTP/1.1000 200 OK", 1, 1000, 200, "OK" },
|
||||
{ "HTTP/1000.1000 200 OK", 1000, 1000, 200, "OK" },
|
||||
{ "HTTP/00001.1 200 OK", 1, 1, 200, "OK" },
|
||||
{ "HTTP/1.00001 200 OK", 1, 1, 200, "OK" },
|
||||
{ "HTTP/99.99 999 99999", 99, 99, 999, "99999" },
|
||||
{ "HTTP/1.1 100 ", 1, 1, 100, "" },
|
||||
|
||||
/* these aren't really valid but we should be able to parse them. */
|
||||
{ "HTTP/1.1 100", 1, 1, 100, "" },
|
||||
{ "HTTP/1.1 200 OK", 1, 1, 200, "OK" },
|
||||
{ "HTTP/1.1 200 \t OK", 1, 1, 200, "OK" },
|
||||
{ " HTTP/1.1 200 OK", 1, 1, 200, "OK" },
|
||||
{ "Norman is a dog HTTP/1.1 200 OK", 1, 1, 200, "OK" },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static const char *bad_sl[] = {
|
||||
"",
|
||||
"HTTP/1.1 1000 OK",
|
||||
"HTTP/1.1 1000",
|
||||
"HTTP/-1.1 100 OK",
|
||||
"HTTP/1.1 -100 OK",
|
||||
"HTTP/ 200 OK",
|
||||
"HTTP/",
|
||||
"HTTP/1.1A 100 OK",
|
||||
"HTTP/1.",
|
||||
"HTTP/1.1 1",
|
||||
"Fish/1.1 100 OK",
|
||||
"HTTP/1.1 10",
|
||||
"HTTP",
|
||||
"H\0TP/1.1 100 OK",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int status_lines(void)
|
||||
{
|
||||
ne_status s;
|
||||
int n;
|
||||
|
||||
for (n = 0; accept_sl[n].status != NULL; n++) {
|
||||
ONV(ne_parse_statusline(accept_sl[n].status, &s),
|
||||
("valid #%d: parse", n));
|
||||
ONV(accept_sl[n].major != s.major_version, ("valid #%d: major", n));
|
||||
ONV(accept_sl[n].minor != s.minor_version, ("valid #%d: minor", n));
|
||||
ONV(accept_sl[n].code != s.code, ("valid #%d: code", n));
|
||||
ONV(strcmp(accept_sl[n].rp, s.reason_phrase),
|
||||
("valid #%d: reason phrase", n));
|
||||
ne_free(s.reason_phrase);
|
||||
}
|
||||
|
||||
for (n = 0; bad_sl[n] != NULL; n++) {
|
||||
ONV(ne_parse_statusline(bad_sl[n], &s) == 0,
|
||||
("invalid #%d", n));
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* Write MD5 of 'len' bytes of 'str' to 'digest' */
|
||||
static unsigned char *digest_md5(const char *data, size_t len, unsigned char digest[16])
|
||||
{
|
||||
struct ne_md5_ctx ctx;
|
||||
|
||||
#define CHUNK 100
|
||||
ne_md5_init_ctx(&ctx);
|
||||
/* exercise the buffering interface */
|
||||
while (len > CHUNK) {
|
||||
ne_md5_process_bytes(data, CHUNK, &ctx);
|
||||
len -= CHUNK;
|
||||
data += CHUNK;
|
||||
}
|
||||
ne_md5_process_bytes(data, len, &ctx);
|
||||
ne_md5_finish_ctx(&ctx, digest);
|
||||
|
||||
return digest;
|
||||
}
|
||||
|
||||
static int md5(void)
|
||||
{
|
||||
unsigned char buf[17] = {0}, buf2[17] = {0};
|
||||
char ascii[33] = {0};
|
||||
char zzzs[500];
|
||||
|
||||
ne_md5_to_ascii(digest_md5("", 0, buf), ascii);
|
||||
ONN("MD5(null)", strcmp(ascii, "d41d8cd98f00b204e9800998ecf8427e"));
|
||||
|
||||
ne_md5_to_ascii(digest_md5("foobar", 7, buf), ascii);
|
||||
ONN("MD5(foobar)", strcmp(ascii, "b4258860eea29e875e2ee4019763b2bb"));
|
||||
|
||||
/* $ perl -e 'printf "z"x500' | md5sum
|
||||
* 8b9323bd72250ea7f1b2b3fb5046391a - */
|
||||
memset(zzzs, 'z', sizeof zzzs);
|
||||
ne_md5_to_ascii(digest_md5(zzzs, sizeof zzzs, buf), ascii);
|
||||
ONN("MD5(\"z\"x512)", strcmp(ascii, "8b9323bd72250ea7f1b2b3fb5046391a"));
|
||||
|
||||
ne_ascii_to_md5(ascii, buf2);
|
||||
ON(memcmp(buf, buf2, 16));
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int md5_alignment(void)
|
||||
{
|
||||
char *bb = ne_malloc(66);
|
||||
struct ne_md5_ctx ctx;
|
||||
|
||||
/* regression test for a bug in md5.c in <0.15.0 on SPARC, where
|
||||
* the process_bytes function would SIGBUS if the buffer argument
|
||||
* isn't 32-bit aligned. Won't trigger on x86 though. */
|
||||
ne_md5_init_ctx(&ctx);
|
||||
ne_md5_process_bytes(bb + 1, 65, &ctx);
|
||||
ne_free(bb);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static const struct {
|
||||
const char *str;
|
||||
time_t time;
|
||||
enum { d_rfc1123, d_iso8601, d_rfc1036 } type;
|
||||
} good_dates[] = {
|
||||
{ "Fri, 08 Jun 2001 22:59:46 GMT", 992041186, d_rfc1123 },
|
||||
{ "Friday, 08-Jun-01 22:59:46 GMT", 992041186, d_rfc1036 },
|
||||
{ "Wednesday, 06-Jun-01 22:59:46 GMT", 991868386, d_rfc1036 },
|
||||
/* some different types of ISO8601 dates. */
|
||||
{ "2001-06-08T22:59:46Z", 992041186, d_iso8601 },
|
||||
{ "2001-06-08T22:59:46.9Z", 992041186, d_iso8601 },
|
||||
{ "2001-06-08T26:00:46+03:01", 992041186, d_iso8601 },
|
||||
{ "2001-06-08T20:58:46-02:01", 992041186, d_iso8601 },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static int parse_dates(void)
|
||||
{
|
||||
int n;
|
||||
|
||||
for (n = 0; good_dates[n].str != NULL; n++) {
|
||||
time_t res;
|
||||
const char *str = good_dates[n].str;
|
||||
|
||||
switch (good_dates[n].type) {
|
||||
case d_rfc1036: res = ne_rfc1036_parse(str); break;
|
||||
case d_iso8601: res = ne_iso8601_parse(str); break;
|
||||
case d_rfc1123: res = ne_rfc1123_parse(str); break;
|
||||
default: res = -1; break;
|
||||
}
|
||||
|
||||
ONV(res == -1, ("date %d parse", n));
|
||||
|
||||
#define FT "%" NE_FMT_TIME_T
|
||||
ONV(res != good_dates[n].time, (
|
||||
"date %d incorrect (" FT " not " FT ")", n,
|
||||
res, good_dates[n].time));
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* trigger segfaults in ne_rfc1036_parse() in <=0.24.5. */
|
||||
static int regress_dates(void)
|
||||
{
|
||||
static const char *dates[] = {
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
};
|
||||
size_t n;
|
||||
|
||||
for (n = 0; n < sizeof(dates)/sizeof(dates[0]); n++) {
|
||||
ne_rfc1036_parse(dates[n]);
|
||||
ne_iso8601_parse(dates[n]);
|
||||
ne_rfc1123_parse(dates[n]);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
#define GOOD(n,m,msg) ONV(ne_version_match(n,m), \
|
||||
("match of " msg " failed (%d.%d)", n, m))
|
||||
#define BAD(n,m,msg) ONV(ne_version_match(n,m) == 0, \
|
||||
("match of " msg " succeeded (%d.%d)", n, m))
|
||||
|
||||
static int versioning(void)
|
||||
{
|
||||
GOOD(NE_VERSION_MAJOR, NE_VERSION_MINOR, "current version");
|
||||
BAD(NE_VERSION_MAJOR + 1, 0, "later major");
|
||||
BAD(NE_VERSION_MAJOR, NE_VERSION_MINOR + 1, "later minor");
|
||||
#if NE_VERSION_MAJOR > 0
|
||||
BAD(NE_VERSION_MAJOR - 1, 0, "earlier major");
|
||||
#if NE_VERSION_MINOR > 0
|
||||
GOOD(NE_VERSION_MAJOR, NE_VERSION_MINOR - 1, "earlier minor");
|
||||
#endif /* NE_VERSION_MINOR > 0 */
|
||||
#else /* where NE_VERSION_MAJOR == 0 */
|
||||
BAD(0, NE_VERSION_MINOR - 1, "earlier minor for 0.x");
|
||||
#endif
|
||||
return OK;
|
||||
}
|
||||
|
||||
#undef GOOD
|
||||
#undef BAD
|
||||
|
||||
/* basic ne_version_string() sanity tests */
|
||||
static int version_string(void)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
ne_snprintf(buf, sizeof buf, "%s", ne_version_string());
|
||||
|
||||
NE_DEBUG(NE_DBG_HTTP, "Version string: %s\n", buf);
|
||||
|
||||
ONN("version string too long", strlen(buf) > 200);
|
||||
ONN("version string contained newline", strchr(buf, '\n') != NULL);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int support(void)
|
||||
{
|
||||
#ifdef NE_HAVE_SSL
|
||||
ONN("SSL support not advertised", !ne_has_support(NE_FEATURE_SSL));
|
||||
#else
|
||||
ONN("SSL support advertised", ne_has_support(NE_FEATURE_SSL));
|
||||
#endif
|
||||
#ifdef NE_HAVE_ZLIB
|
||||
ONN("zlib support not advertised", !ne_has_support(NE_FEATURE_ZLIB));
|
||||
#else
|
||||
ONN("zlib support advertised", ne_has_support(NE_FEATURE_ZLIB));
|
||||
#endif
|
||||
#ifdef NE_HAVE_IPV6
|
||||
ONN("IPv6 support not advertised", !ne_has_support(NE_FEATURE_IPV6));
|
||||
#else
|
||||
ONN("IPv6 support advertised", ne_has_support(NE_FEATURE_IPV6));
|
||||
#endif
|
||||
#ifdef NE_HAVE_LFS
|
||||
ONN("LFS support not advertised", !ne_has_support(NE_FEATURE_LFS));
|
||||
#else
|
||||
ONN("LFS support advertised", ne_has_support(NE_FEATURE_LFS));
|
||||
#endif
|
||||
return OK;
|
||||
}
|
||||
|
||||
ne_test tests[] = {
|
||||
T(status_lines),
|
||||
T(md5),
|
||||
T(md5_alignment),
|
||||
T(parse_dates),
|
||||
T(regress_dates),
|
||||
T(versioning),
|
||||
T(version_string),
|
||||
T(support),
|
||||
T(NULL)
|
||||
};
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
Utility functions for HTTP client tests
|
||||
Copyright (C) 2001-2004, Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h> /* for sleep() */
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#include "ne_session.h"
|
||||
|
||||
#include "child.h"
|
||||
#include "tests.h"
|
||||
#include "utils.h"
|
||||
|
||||
int make_session(ne_session **sess, server_fn fn, void *ud)
|
||||
{
|
||||
*sess = ne_session_create("http", "localhost", 7777);
|
||||
return spawn_server(7777, fn, ud);
|
||||
}
|
||||
|
||||
static int serve_response(ne_socket *s, const char *response)
|
||||
{
|
||||
CALL(discard_request(s));
|
||||
CALL(discard_body(s));
|
||||
ONN("failed to send response", SEND_STRING(s, response));
|
||||
return OK;
|
||||
}
|
||||
|
||||
int single_serve_string(ne_socket *s, void *userdata)
|
||||
{
|
||||
const char *str = userdata;
|
||||
return serve_response(s, str);
|
||||
}
|
||||
|
||||
int sleepy_server(ne_socket *sock, void *userdata)
|
||||
{
|
||||
sleep(10);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int many_serve_string(ne_socket *s, void *userdata)
|
||||
{
|
||||
int n;
|
||||
struct many_serve_args *args = userdata;
|
||||
|
||||
for (n = 0; n < args->count; n++) {
|
||||
NE_DEBUG(NE_DBG_HTTP, "Serving response %d\n", n);
|
||||
CALL(serve_response(s, args->str));
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int any_request(ne_session *sess, const char *uri)
|
||||
{
|
||||
ne_request *req = ne_request_create(sess, "GET", uri);
|
||||
int ret = ne_request_dispatch(req);
|
||||
ne_request_destroy(req);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int any_2xx_request(ne_session *sess, const char *uri)
|
||||
{
|
||||
ne_request *req = ne_request_create(sess, "GET", uri);
|
||||
int ret = ne_request_dispatch(req);
|
||||
int klass = ne_get_status(req)->klass;
|
||||
ne_request_destroy(req);
|
||||
ONV(ret != NE_OK || klass != 2,
|
||||
("request failed: %s", ne_get_error(sess)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
int any_2xx_request_body(ne_session *sess, const char *uri)
|
||||
{
|
||||
ne_request *req = ne_request_create(sess, "GET", uri);
|
||||
#define BSIZE 5000
|
||||
char *body = memset(ne_malloc(BSIZE), 'A', BSIZE);
|
||||
int ret;
|
||||
ne_set_request_body_buffer(req, body, BSIZE);
|
||||
ret = ne_request_dispatch(req);
|
||||
ne_free(body);
|
||||
ONV(ret != NE_OK || ne_get_status(req)->klass != 2,
|
||||
("request failed: %s", ne_get_error(sess)));
|
||||
ne_request_destroy(req);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int serve_sstring(ne_socket *sock, void *ud)
|
||||
{
|
||||
struct string *str = ud;
|
||||
|
||||
NE_DEBUG(NE_DBG_SOCKET, "Serving string: [[[%.*s]]]\n",
|
||||
(int)str->len, str->data);
|
||||
|
||||
ONN("write failed", ne_sock_fullwrite(sock, str->data, str->len));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int serve_sstring_slowly(ne_socket *sock, void *ud)
|
||||
{
|
||||
struct string *str = ud;
|
||||
size_t n;
|
||||
|
||||
NE_DEBUG(NE_DBG_SOCKET, "Slowly serving string: [[[%.*s]]]\n",
|
||||
(int)str->len, str->data);
|
||||
|
||||
for (n = 0; n < str->len; n++) {
|
||||
ONN("write failed", ne_sock_fullwrite(sock, &str->data[n], 1));
|
||||
minisleep();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int serve_infinite(ne_socket *sock, void *ud)
|
||||
{
|
||||
struct infinite *i = ud;
|
||||
|
||||
CALL(discard_request(sock));
|
||||
|
||||
SEND_STRING(sock, i->header);
|
||||
|
||||
while (server_send(sock, i->repeat, strlen(i->repeat)) == 0)
|
||||
/* nullop */;
|
||||
|
||||
return OK;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
neon-specific test utils
|
||||
Copyright (C) 2001-2005, Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H 1
|
||||
|
||||
#include "ne_request.h"
|
||||
|
||||
#include "child.h"
|
||||
|
||||
#define ONREQ(x) do { int _ret = (x); if (_ret) { t_context("line %d: HTTP error:\n%s", __LINE__, ne_get_error(sess)); return FAIL; } } while (0);
|
||||
|
||||
int single_serve_string(ne_socket *s, void *userdata);
|
||||
|
||||
struct many_serve_args {
|
||||
int count;
|
||||
const char *str;
|
||||
};
|
||||
|
||||
/* Serves args->str response args->count times down a single
|
||||
* connection. */
|
||||
int many_serve_string(ne_socket *s, void *userdata);
|
||||
|
||||
/* Run a request using URI on the session. */
|
||||
int any_request(ne_session *sess, const char *uri);
|
||||
|
||||
/* Run a request using URI on the session; fail on a non-2xx response.
|
||||
*/
|
||||
int any_2xx_request(ne_session *sess, const char *uri);
|
||||
|
||||
/* As above but with a request body. */
|
||||
int any_2xx_request_body(ne_session *sess, const char *uri);
|
||||
|
||||
/* makes *session, spawns server which will run 'fn(userdata,
|
||||
* socket)'. sets error context if returns non-zero, i.e use like:
|
||||
* CALL(make_session(...)); */
|
||||
int make_session(ne_session **sess, server_fn fn, void *userdata);
|
||||
|
||||
/* Server which sleeps for 10 seconds then closes the socket. */
|
||||
int sleepy_server(ne_socket *sock, void *userdata);
|
||||
|
||||
struct string {
|
||||
char *data;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
/* Serve a struct string. */
|
||||
int serve_sstring(ne_socket *sock, void *ud);
|
||||
|
||||
/* Serve a struct string slowly. */
|
||||
int serve_sstring_slowly(ne_socket *sock, void *ud);
|
||||
|
||||
struct infinite {
|
||||
const char *header, *repeat;
|
||||
};
|
||||
|
||||
/* Pass a "struct infinite *" as userdata, this function sends
|
||||
* ->header and then loops sending ->repeat forever. */
|
||||
int serve_infinite(ne_socket *sock, void *ud);
|
||||
|
||||
#endif /* UTILS_H */
|
||||
@@ -0,0 +1,514 @@
|
||||
/*
|
||||
neon test suite
|
||||
Copyright (C) 2002-2004, Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "ne_xml.h"
|
||||
|
||||
#include "tests.h"
|
||||
#include "child.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define ABORT (-42) /* magic code for abort handlers */
|
||||
|
||||
/* A set of SAX handlers which serialize SAX events back into a
|
||||
* pseudo-XML-like string. */
|
||||
static int startelm(void *userdata, int state,
|
||||
const char *nspace, const char *name,
|
||||
const char **atts)
|
||||
{
|
||||
ne_buffer *buf = userdata;
|
||||
int n;
|
||||
|
||||
if (strcmp(name, "decline") == 0)
|
||||
return NE_XML_DECLINE;
|
||||
|
||||
ne_buffer_concat(buf, "<", "{", nspace, "}", name, NULL);
|
||||
for (n = 0; atts && atts[n] != NULL; n+=2) {
|
||||
ne_buffer_concat(buf, " ", atts[n], "='", atts[n+1], "'", NULL);
|
||||
}
|
||||
ne_buffer_zappend(buf, ">");
|
||||
|
||||
return state + 1;
|
||||
}
|
||||
|
||||
static int chardata(void *userdata, int state, const char *cdata, size_t len)
|
||||
{
|
||||
ne_buffer *buf = userdata;
|
||||
ne_buffer_append(buf, cdata, len);
|
||||
return strncmp(cdata, "!ABORT!", len) == 0 ? ABORT : NE_XML_DECLINE;
|
||||
}
|
||||
|
||||
static int endelm(void *userdata, int state,
|
||||
const char *nspace, const char *name)
|
||||
{
|
||||
ne_buffer *buf = userdata;
|
||||
ne_buffer_concat(buf, "</{", nspace, "}", name, ">", NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* A set of SAX handlers which do as above, but change some element
|
||||
* names; used to check nested SAX handling is working properly. */
|
||||
static int startelm_xform(void *userdata, int state,
|
||||
const char *nspace, const char *name,
|
||||
const char **atts)
|
||||
{
|
||||
if (strcmp(nspace, "two") == 0)
|
||||
return startelm(userdata, state, nspace, "xform", atts);
|
||||
else
|
||||
return NE_XML_DECLINE;
|
||||
}
|
||||
|
||||
static int endelm_xform(void *userdata, int state,
|
||||
const char *nspace, const char *name)
|
||||
{
|
||||
if (strcmp(nspace, "two") == 0)
|
||||
return endelm(userdata, state, nspace, "xform");
|
||||
else
|
||||
return NE_XML_DECLINE;
|
||||
}
|
||||
|
||||
/* A set of SAX handlers which verify that state handling is working
|
||||
* correctly. */
|
||||
static int startelm_state(void *userdata, int parent,
|
||||
const char *nspace, const char *name,
|
||||
const char **atts)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (strcmp(nspace, "state") != 0)
|
||||
return NE_XML_DECLINE;
|
||||
|
||||
for (n = 0; atts[n]; n += 2) {
|
||||
if (strcmp(atts[n], "parent") == 0) {
|
||||
int expected = atoi(atts[n+1]);
|
||||
|
||||
if (expected != parent) {
|
||||
char err[50];
|
||||
sprintf(err, "parent state of %s was %d not %d", name, parent,
|
||||
expected);
|
||||
ne_buffer_zappend(userdata, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return atoi(name+1);
|
||||
}
|
||||
|
||||
static int endelm_state(void *userdata, int state,
|
||||
const char *nspace, const char *name)
|
||||
{
|
||||
int expected = atoi(name + 1);
|
||||
ne_buffer *buf = userdata;
|
||||
|
||||
if (state != expected)
|
||||
ne_buffer_concat(buf, "wrong state in endelm of ", name, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* A set of SAX handlers which verify that abort handling is working
|
||||
* correctly. */
|
||||
static int startelm_abort(void *buf, int parent,
|
||||
const char *nspace, const char *name,
|
||||
const char **atts)
|
||||
{
|
||||
if (strcmp(name, "abort-start") == 0) {
|
||||
ne_buffer_zappend(buf, "ABORT");
|
||||
return ABORT;
|
||||
} else
|
||||
return startelm(buf, parent, nspace, name, atts);
|
||||
}
|
||||
|
||||
static int endelm_abort(void *buf, int state,
|
||||
const char *nspace, const char *name)
|
||||
{
|
||||
if (strcmp(name, "abort-end") == 0) {
|
||||
ne_buffer_zappend(buf, "ABORT");
|
||||
return ABORT;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Test mode for parse_match: */
|
||||
enum match_type {
|
||||
match_valid = 0, /* test that the parse succeeds */
|
||||
match_invalid, /* test that the parse fails */
|
||||
match_nohands, /* test with no handlers registered */
|
||||
match_encoding, /* test whether the encoding is equal to the result string */
|
||||
match_chunked /* parse the document one byte at a time */
|
||||
};
|
||||
|
||||
static int parse_match(const char *doc, const char *result, enum match_type t)
|
||||
{
|
||||
ne_xml_parser *p = ne_xml_create();
|
||||
ne_buffer *buf = ne_buffer_create();
|
||||
int ret;
|
||||
|
||||
if (t == match_invalid)
|
||||
ne_xml_push_handler(p, startelm_abort, chardata, endelm_abort, buf);
|
||||
if (t != match_encoding && t != match_nohands) {
|
||||
ne_xml_push_handler(p, startelm_state, NULL, endelm_state, buf);
|
||||
ne_xml_push_handler(p, startelm, chardata, endelm, buf);
|
||||
ne_xml_push_handler(p, startelm_xform, chardata, endelm_xform, buf);
|
||||
}
|
||||
|
||||
if (t == match_chunked) {
|
||||
do {
|
||||
ret = ne_xml_parse(p, doc++, 1);
|
||||
} while (ret == 0 && *doc);
|
||||
} else {
|
||||
ret = ne_xml_parse(p, doc, strlen(doc));
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
ne_xml_parse(p, "", 0);
|
||||
}
|
||||
|
||||
ONV(ret != ne_xml_failed(p),
|
||||
("ne_xml_failed gave %d not %d", ne_xml_failed(p), ret));
|
||||
|
||||
if (t == match_invalid)
|
||||
ONV(ret != ABORT,
|
||||
("parse got %d not abort failure: %s", ret, buf->data));
|
||||
else
|
||||
ONV(ret, ("parse failed: %s", ne_xml_get_error(p)));
|
||||
|
||||
if (t == match_encoding) {
|
||||
const char *enc = ne_xml_doc_encoding(p);
|
||||
ONV(strcmp(enc, result), ("encoding was `%s' not `%s'", enc, result));
|
||||
} else if (t == match_valid)
|
||||
ONV(strcmp(result, buf->data),
|
||||
("result mismatch: %s not %s", buf->data, result));
|
||||
|
||||
ne_xml_destroy(p);
|
||||
ne_buffer_destroy(buf);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int matches(void)
|
||||
{
|
||||
#define PFX "<?xml version='1.0' encoding='utf-8'?>\r\n"
|
||||
#define E(ns, n) "<{" ns "}" n "></{" ns "}" n ">"
|
||||
static const struct {
|
||||
const char *in, *out;
|
||||
enum match_type invalid;
|
||||
} ms[] = {
|
||||
|
||||
/*** Simplest tests ***/
|
||||
{ PFX "<hello/>", "<{}hello></{}hello>"},
|
||||
{ PFX "<hello foo='bar'/>",
|
||||
"<{}hello foo='bar'></{}hello>"},
|
||||
|
||||
/*** Tests for character data handling. ***/
|
||||
{ PFX "<hello> world</hello>", "<{}hello> world</{}hello>"},
|
||||
/* test for cdata between elements. */
|
||||
{ PFX "<hello>\r\n<wide> world</wide></hello>",
|
||||
"<{}hello>\n<{}wide> world</{}wide></{}hello>"},
|
||||
|
||||
/* UTF-8 XML Byte Order Mark */
|
||||
{ "\xEF\xBB\xBF" PFX "<hello/>", "<{}hello></{}hello>" },
|
||||
/* UTF-8 XML Byte Order Mark */
|
||||
{ "\xEF\xBB\xBF" PFX "<hello/>", "<{}hello></{}hello>", match_chunked },
|
||||
/* UTF-8 XML Byte Order Mark sans prolog */
|
||||
{ "\xEF\xBB\xBF" "<hello/>", "<{}hello></{}hello>" },
|
||||
|
||||
/*** Tests for namespace handling. ***/
|
||||
#define NSA "xmlns:foo='bar'"
|
||||
{ PFX "<foo:widget " NSA "/>",
|
||||
"<{bar}widget " NSA ">"
|
||||
"</{bar}widget>" },
|
||||
/* inherited namespace expansion. */
|
||||
{ PFX "<widget " NSA "><foo:norman/></widget>",
|
||||
"<{}widget " NSA ">" E("bar", "norman") "</{}widget>"},
|
||||
{ PFX "<widget " NSA " xmlns:abc='def' xmlns:g='z'>"
|
||||
"<foo:norman/></widget>",
|
||||
"<{}widget " NSA " xmlns:abc='def' xmlns:g='z'>"
|
||||
E("bar", "norman") "</{}widget>"},
|
||||
/* empty namespace default takes precedence. */
|
||||
{ PFX "<widget xmlns='foo'><smidgen xmlns=''><norman/>"
|
||||
"</smidgen></widget>",
|
||||
"<{foo}widget xmlns='foo'><{}smidgen xmlns=''>"
|
||||
E("", "norman")
|
||||
"</{}smidgen></{foo}widget>" },
|
||||
/* inherited empty namespace default */
|
||||
{ PFX "<bar xmlns='foo'><grok xmlns=''><fish/></grok></bar>",
|
||||
"<{foo}bar xmlns='foo'><{}grok xmlns=''>"
|
||||
E("", "fish") "</{}grok></{foo}bar>" },
|
||||
|
||||
/* regression test for neon <= 0.23.5 with libxml2, where the
|
||||
* "dereference entities" flag was not set by default. */
|
||||
{ PFX "<widget foo=\"no&body\"/>",
|
||||
"<{}widget foo='no&body'></{}widget>" },
|
||||
{ PFX "<widget foo=\"no body\"/>",
|
||||
"<{}widget foo='no body'></{}widget>" },
|
||||
|
||||
/* tests for declined branches */
|
||||
{ PFX "<hello><decline>fish</decline>"
|
||||
"<world><decline/>yes</world>goodbye<decline/></hello>",
|
||||
"<{}hello><{}world>yes</{}world>goodbye</{}hello>" },
|
||||
{ PFX
|
||||
"<hello><decline><nested>fish</nested>bar</decline><fish/></hello>",
|
||||
"<{}hello>" E("", "fish") "</{}hello>" },
|
||||
/* tests for nested SAX handlers */
|
||||
{ PFX "<hello xmlns='two'><decline/></hello>",
|
||||
"<{two}hello xmlns='two'>" E("two", "xform") "</{two}hello>"},
|
||||
|
||||
/* tests for state handling */
|
||||
{ PFX "<a55 xmlns='state'/>", "" },
|
||||
{ PFX "<a777 xmlns='state' parent='0'/>", "" },
|
||||
{ PFX "<a99 xmlns='state'><f77 parent='99'/>blah</a99>", "" },
|
||||
|
||||
/* tests for abort handling */
|
||||
{ PFX "<hello><merry><abort-start/></merry></hello>",
|
||||
"<{}hello><{}merry>ABORT", match_invalid },
|
||||
{ PFX "<hello><merry><abort-end/>fish</merry></hello>",
|
||||
"<{}hello><{}merry><{}abort-end>ABORT", match_invalid },
|
||||
{ PFX "<hello>!ABORT!</hello>", "<{}hello>!ABORT!", match_invalid },
|
||||
{ PFX "<hello>!ABORT!<foo/></hello>", "<{}hello>!ABORT!", match_invalid },
|
||||
{ PFX "<hello>!ABORT!</fish>", "<{}hello>!ABORT!", match_invalid },
|
||||
|
||||
/* tests for encodings */
|
||||
{ "<?xml version='1.0' encoding='ISO-8859-1'?><hello/>",
|
||||
"ISO-8859-1", match_encoding },
|
||||
|
||||
{ "<?xml version='1.0' encoding='UTF-8'?><hello/>",
|
||||
"UTF-8", match_encoding },
|
||||
|
||||
/* test that parse is valid even with no handlers registered. */
|
||||
{ PFX "<hello><old>world</old></hello>", "", match_nohands },
|
||||
|
||||
/* regression test for prefix matching bug fixed in 0.18.0 */
|
||||
#define THENS "xmlns:d='foo' xmlns:dd='bar'"
|
||||
{ PFX "<d:hello " THENS "/>",
|
||||
"<{foo}hello " THENS "></{foo}hello>" },
|
||||
|
||||
/**** end of list ****/ { NULL, NULL }
|
||||
};
|
||||
int n;
|
||||
|
||||
for (n = 0; ms[n].in != NULL; n++) {
|
||||
CALL(parse_match(ms[n].in, ms[n].out, ms[n].invalid));
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int mapping(void)
|
||||
{
|
||||
static const struct ne_xml_idmap map[] = {
|
||||
{ "fee", "bar", 1 },
|
||||
{ "foo", "bar", 2 },
|
||||
{ "bar", "foo", 3 },
|
||||
{ "", "bob", 4 },
|
||||
{ "balloon", "buffoon", 5},
|
||||
{ NULL, NULL, 0}
|
||||
};
|
||||
int n;
|
||||
|
||||
for (n = 0; map[n].id; n++) {
|
||||
int id = ne_xml_mapid(map, NE_XML_MAPLEN(map) - 1,
|
||||
map[n].nspace, map[n].name);
|
||||
ONV(id != map[n].id, ("mapped to id %d not %d", id, map[n].id));
|
||||
}
|
||||
|
||||
n = ne_xml_mapid(map, NE_XML_MAPLEN(map) - 1, "no-such", "element");
|
||||
ONV(n != 0, ("unknown element got id %d not zero", n));
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* Test for some parse failures */
|
||||
static int fail_parse(void)
|
||||
{
|
||||
static const char *docs[] = {
|
||||
"foo",
|
||||
PFX "<bar:foo/>",
|
||||
/* malformed namespace declarations */
|
||||
PFX "<foo xmlns:D=''/>",
|
||||
PFX "<foo xmlns:='fish'/>",
|
||||
PFX "<foo xmlns:.bar='fish'/>",
|
||||
PFX "<foo xmlns:-bar='fish'/>",
|
||||
PFX "<foo xmlns:0bar='fish'/>",
|
||||
PFX "<fee xmlns:8baz='bar'/>",
|
||||
|
||||
/* element names which are not valid QNames. */
|
||||
PFX "<foo: xmlns:foo='bar'/>",
|
||||
PFX "<:fee/>",
|
||||
PFX "<0fish/>",
|
||||
PFX "<foo:0fish xmlns:foo='bar'/>",
|
||||
PFX "<foo:9fish xmlns:foo='bar'/>",
|
||||
PFX "<foo:-fish xmlns:foo='bar'/>",
|
||||
PFX "<foo:.fish xmlns:foo='bar'/>",
|
||||
|
||||
#if 0 /* currently disabled to allow SVN to work */
|
||||
PFX "<foo:bar:baz xmlns:foo='bar'/>",
|
||||
PFX "<fee xmlns:baz:bar='bar'/>",
|
||||
PFX "<fee xmlns::bar='bar'/>",
|
||||
PFX "<foo::fish xmlns:foo='bar'/>",
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
/* 2-byte encoding of '.': */
|
||||
PFX "<foo>" "\x2F\xC0\xAE\x2E\x2F" "</foo>",
|
||||
/* 3-byte encoding of '.': */
|
||||
PFX "<foo>" "\x2F\xE0\x80\xAE\x2E\x2F" "</foo>",
|
||||
/* 4-byte encoding of '.': */
|
||||
PFX "<foo>" "\x2F\xF0\x80\x80\xAE\x2E\x2F" "</foo>",
|
||||
/* 5-byte encoding of '.': */
|
||||
PFX "<foo>" "\x2F\xF8\x80\x80\x80\xAE\x2E\x2F" "</foo>",
|
||||
/* 6-byte encoding of '.': */
|
||||
PFX "<foo>" "\x2F\xFC\x80\x80\x80\x80\xAE\x2E\x2F" "</foo>",
|
||||
#endif
|
||||
/* two-byte encoding of '<' must not be parsed as a '<': */
|
||||
PFX "\xC0\xBC" "foo></foo>",
|
||||
|
||||
/* Invalid UTF-8 XML Byte Order Marks */
|
||||
"\xEF\xBB" PFX "<hello/>",
|
||||
"\xEF" PFX "<hello/>",
|
||||
|
||||
NULL
|
||||
};
|
||||
int n;
|
||||
|
||||
for (n = 0; docs[n]; n++) {
|
||||
ne_xml_parser *p = ne_xml_create();
|
||||
const char *err;
|
||||
|
||||
ne_xml_parse(p, docs[n], strlen(docs[n]));
|
||||
ne_xml_parse(p, "", 0);
|
||||
ONV(ne_xml_failed(p) <= 0,
|
||||
("`%s' did not get positive parse error", docs[n]));
|
||||
|
||||
err = ne_xml_get_error(p);
|
||||
NE_DEBUG(NE_DBG_HTTP, "Parse error for '%s': %s\n", docs[n], err);
|
||||
ONV(strstr(err, "parse error") == NULL
|
||||
&& strstr(err, "Invalid Byte Order Mark") == NULL,
|
||||
("bad error %s", err));
|
||||
|
||||
ne_xml_destroy(p);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int check_attrib(ne_xml_parser *p, const char **atts,
|
||||
const char *nspace, const char *name,
|
||||
const char *value)
|
||||
{
|
||||
const char *act = ne_xml_get_attr(p, atts, nspace, name);
|
||||
char err[50];
|
||||
int ret = 0;
|
||||
|
||||
if (value == NULL) {
|
||||
if (act != NULL) {
|
||||
sprintf(err, "attribute %s was set to %s", name, act);
|
||||
ret = NE_XML_ABORT;
|
||||
}
|
||||
} else {
|
||||
if (act == NULL) {
|
||||
sprintf(err, "attribute %s not found", name);
|
||||
ret = NE_XML_ABORT;
|
||||
} else if (strcmp(act, value) != 0) {
|
||||
sprintf(err, "attribute %s was %s not %s", name, act, value);
|
||||
ret = NE_XML_ABORT;
|
||||
}
|
||||
}
|
||||
if (ret == NE_XML_ABORT) ne_xml_set_error(p, err);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int startelm_attrib(void *userdata, int state,
|
||||
const char *nspace, const char *name,
|
||||
const char **atts)
|
||||
{
|
||||
ne_xml_parser *p = userdata;
|
||||
|
||||
if (strcmp(name, "hello") == 0) {
|
||||
CALL(check_attrib(p, atts, NULL, "first", "second"));
|
||||
CALL(check_attrib(p, atts, NULL, "third", ""));
|
||||
CALL(check_attrib(p, atts, "garth", "bar", "asda"));
|
||||
CALL(check_attrib(p, atts, "giraffe", "bar", NULL));
|
||||
CALL(check_attrib(p, atts, "hot", "dog", NULL));
|
||||
CALL(check_attrib(p, atts, NULL, "nonesuch", NULL));
|
||||
} else if (strcmp(name, "goodbye") == 0) {
|
||||
if (atts[0] != NULL) {
|
||||
ne_xml_set_error(p, "non-empty attrib array");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int attributes(void)
|
||||
{
|
||||
ne_xml_parser *p = ne_xml_create();
|
||||
static const char doc[] = PFX
|
||||
"<hello xmlns:foo='garth' first='second' third='' "
|
||||
"foo:bar='asda' goof:bar='jar'><goodbye/></hello>";
|
||||
|
||||
ne_xml_push_handler(p, startelm_attrib, NULL, NULL, p);
|
||||
|
||||
ne_xml_parse_v(p, doc, strlen(doc));
|
||||
|
||||
ONV(ne_xml_failed(p), ("parse error: %s", ne_xml_get_error(p)));
|
||||
|
||||
ne_xml_destroy(p);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* Test for the get/set error interface */
|
||||
static int errors(void)
|
||||
{
|
||||
ne_xml_parser *p = ne_xml_create();
|
||||
const char *err;
|
||||
|
||||
ONV(strcmp(ne_xml_get_error(p), "Unknown error") != 0,
|
||||
("initial error string unspecified"));
|
||||
|
||||
ne_xml_set_error(p, "Fish food");
|
||||
err = ne_xml_get_error(p);
|
||||
|
||||
ONV(strcmp(err, "Fish food"), ("wrong error %s!", err));
|
||||
|
||||
ne_xml_destroy(p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ne_test tests[] = {
|
||||
T(matches),
|
||||
T(mapping),
|
||||
T(fail_parse),
|
||||
T(attributes),
|
||||
T(errors),
|
||||
T(NULL)
|
||||
};
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
Test cases for the ne_xmlreq.h interface.
|
||||
Copyright (C) 2005, Joe Orton <joe@manyfish.co.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "ne_xmlreq.h"
|
||||
|
||||
#include "tests.h"
|
||||
#include "utils.h"
|
||||
|
||||
static int success(void)
|
||||
{
|
||||
ne_session *sess;
|
||||
ne_request *req;
|
||||
ne_xml_parser *parser;
|
||||
|
||||
CALL(make_session(&sess, single_serve_string,
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Content-Type: text/xml\r\n"
|
||||
"Connection: close\r\n" "\r\n"
|
||||
"<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||
"<hello/>"));
|
||||
|
||||
req = ne_request_create(sess, "PARSE", "/");
|
||||
parser = ne_xml_create();
|
||||
|
||||
ONREQ(ne_xml_dispatch_request(req, parser));
|
||||
|
||||
ne_xml_destroy(parser);
|
||||
ne_request_destroy(req);
|
||||
ne_session_destroy(sess);
|
||||
return await_server();
|
||||
}
|
||||
|
||||
static int failure(void)
|
||||
{
|
||||
ne_session *sess;
|
||||
ne_request *req;
|
||||
ne_xml_parser *parser;
|
||||
|
||||
CALL(make_session(&sess, single_serve_string,
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Content-Type: text/xml\r\n"
|
||||
"Connection: close\r\n" "\r\n"
|
||||
"<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||
"<hello>"));
|
||||
|
||||
req = ne_request_create(sess, "PARSE", "/");
|
||||
parser = ne_xml_create();
|
||||
|
||||
ONN("XML parse did not fail",
|
||||
ne_xml_dispatch_request(req, parser) == NE_OK);
|
||||
|
||||
NE_DEBUG(NE_DBG_HTTP, "error string: %s\n", ne_get_error(sess));
|
||||
|
||||
ONV(strstr(ne_get_error(sess), "200 OK") != NULL,
|
||||
("no error string set on parse error: '%s'", ne_get_error(sess)));
|
||||
|
||||
ne_xml_destroy(parser);
|
||||
ne_request_destroy(req);
|
||||
ne_session_destroy(sess);
|
||||
return await_server();
|
||||
}
|
||||
|
||||
ne_test tests[] = {
|
||||
T(success),
|
||||
T(failure),
|
||||
T(NULL)
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user