This commit is contained in:
biosvos
2026-08-07 17:38:18 +09:00
commit 873193a243
9613 changed files with 2755992 additions and 0 deletions
@@ -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&amp;body\"/>",
"<{}widget foo='no&body'></{}widget>" },
{ PFX "<widget foo=\"no&#x20;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)
};